go 之用户server服务

docker 部署mysql

docker run -d \
  --name mysql8 \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -e MYSQL_DATABASE=testdb \
  -v mysql_data:/var/lib/mysql \
  --restart unless-stopped \
  mysql:8.0

登录
#docker exec -it mysql8 mysql -uroot -p
#123456
> ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
> FLUSH PRIVILEGES;

  

定义用户表结构

package model

import (
	"time"

	"gorm.io/gorm"
)

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;type:varchar(11);not null"`
	Password string `gorm:"type:varchar(10);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表示管理员'"`

}

  同步表结构到数据库

package main

import (
	"log"
	"os"
	"time"

	"gorm.io/driver/mysql"
	//"google.golang.org/grpc/resolver/dns"
	"mxshop_srvs/user_srv/model"

	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
)

func main() {
	dsn := "root:123456@tcp(192.168.40.25: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{})
}
启动
PS D:\golang\goproject\src\mxshop_srvs> go run user_srv\model\main\main.go

2026/09/04 10:32:42 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:38
[0.508ms] [rows:-] SELECT DATABASE()

2026/09/04 10:32:42 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:38
[4.769ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'mxshop_user_srv%' ORDER BY SCHEMA_NAME='mxshop_user_srv' DESC,SCHEMA_NAME limit 1

2026/09/04 10:32:42 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:38
[5.684ms] [rows:-] SELECT count(*) FROM information_schema.tables WHERE table_schema = 'mxshop_user_srv' AND table_name = 'user' AND table_type = 'BASE TABLE'

2026/09/04 10:32:42 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:38
[1.556ms] [rows:-] SELECT DATABASE()

2026/09/04 10:32:42 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:38
[3.567ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'mxshop_user_srv%' ORDER BY SCHEMA_NAME='mxshop_user_srv' DESC,SCHEMA_NAME limit 1

2026/09/04 10:32:42 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:38
[4.337ms] [rows:-] SELECT * FROM `user` LIMIT 1

2026/09/04 10:32:42 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:38
[4.492ms] [rows:-] SELECT column_name, column_default, is_nullable = 'YES', data_type, character_maximum_length, column_type, column_key, extra, column_comment, numeric_precision, numeric_scale , datetime_precision FROM information_schema.columns WHERE table_schema = 'mxshop_user_srv' AND table_name = 'user' ORDER BY ORDINAL_POSITION

2026/09/04 10:32:42 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:38
[66.351ms] [rows:0] ALTER TABLE `user` MODIFY COLUMN `gender` varchar(6) comment 'female 表示女,male表示男' DEFAULT 'male'

2026/09/04 10:32:42 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:38
[21.451ms] [rows:0] ALTER TABLE `user` MODIFY COLUMN `role` int comment '1表示普通用户 2表示管理员' DEFAULT 1

2026/09/04 10:32:42 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:38
[0.551ms] [rows:-] SELECT DATABASE()

2026/09/04 10:32:42 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:38
[2.323ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'mxshop_user_srv%' ORDER BY SCHEMA_NAME='mxshop_user_srv' DESC,SCHEMA_NAME limit 1

2026/09/04 10:32:42 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:38
[4.359ms] [rows:-] SELECT count(*) FROM information_schema.statistics WHERE table_schema = 'mxshop_user_srv' AND table_name = 'user' AND index_name = 'idx_mobile'

  sysql 查看数据表

image

md5 加密介绍

当然可以。你可以把 MD5 理解成一种“把任意长度的数据,计算成固定长度指纹”的算法。

1. MD5 是什么?

MD5(Message-Digest Algorithm 5) 是一种密码学哈希算法,由 Ronald Rivest 设计。

它的特点是:

无论输入多长,经过 MD5 计算后,都得到固定的 128 bit(16 字节)结果。

通常我们看到的是 32 位十六进制字符串

例如:

hello
经过 MD5:

5d41402abc4b2a76b9719d911017c592
所以可以简单理解为:

任意数据
   ↓
  MD5
   ↓
128 bit
   ↓
32位十六进制字符串

  


2. MD5 最大的特点:不可逆

比如:

password123
        ↓
      MD5
        ↓
482c811da5d5b4bc6d497ffa98491e38
你不能通过正常的 MD5 算法:

482c811da5d5b4bc6d497ffa98491e38
        ↓
       MD5
        ↓
password123
反推出原文。

所以它不是传统意义上的“加密”,更准确地说是:

哈希(Hash)/ 摘要算法

这一点非常重要。

  

加密 vs 哈希

 加密哈希
是否可以还原 可以 不应该可以
是否需要密钥 通常需要 不需要
输入长度 通常可变 可变
输出长度 通常可变 固定
主要用途 保密 完整性、指纹、校验
例子 AES、RSA MD5、SHA-256

3. MD5 为什么是 32 位?

MD5 的原始输出是:

128 bit
而:

1 个十六进制字符 = 4 bit
所以:

128 ÷ 4 = 32
因此我们经常看到:

5d41402abc4b2a76b9719d911017c592
正好 32 个十六进制字符。

  


4. MD5 的一个重要特性:雪崩效应

比如:

hello
和:

Hello
只改变了一个字母,但是 MD5 结果会完全不同。

hello
→ 5d41402abc4b2a76b9719d911017c592

Hello
→ 8b1a9953c4611296a827abf8c47804d7f
这就是哈希算法非常重要的特性:

输入发生很小变化,输出应该发生巨大变化。

  


5. MD5 已经不安全了

这个是你学习 MD5 时一定要注意的。

MD5 已经被证明存在严重的碰撞问题,不应该用于安全密码学场景。

例如攻击者可以构造:

数据 A
   ↓
MD5
   ↓
XXXX

数据 B
   ↓
MD5
   ↓
XXXX
也就是说:

两个不同的数据可能产生相同的 MD5。

这叫做:

碰撞(Collision)

因此现在:

❌ 不建议
MD5(password)
直接保存用户密码。

❌ 不建议
用 MD5 做安全签名。

❌ 不建议
用 MD5 判断高安全级别的数据完整性。

  


6. 那 MD5 现在还有什么用?

虽然 MD5 不适合密码学安全场景,但它并不是完全没用了。

例如一些普通的:

文件指纹
myfile.zip
    ↓
   MD5
    ↓
a7c5...xxx
下载完成后计算 MD5:

服务器文件 MD5
        ↓
a7c5...xxx

本地文件 MD5
        ↓
a7c5...xxx
如果一致,说明文件内容大概率没有发生变化。

不过如果面对恶意攻击者,MD5 已经不能提供可靠的安全保证。

  


7. Go 怎么计算 MD5?

你现在正在学 Go,这个非常值得顺手掌握。

Go 标准库直接提供:

crypto/md5
例如:

package main

import (
	"crypto/md5"
	"fmt"
)

func main() {
	data := []byte("hello")

	result := md5.Sum(data)

	fmt.Printf("%x\n", result)
}
输出:

5d41402abc4b2a76b9719d911017c592
这里:

md5.Sum(data)
返回的是:

[16]byte
因为:

128 bit = 16 byte
然后:

%x
把这 16 个字节按照十六进制打印出来,就变成了:

32个字符

  


8. MD5 的内部原理

如果你现在是在系统学习 Go,我建议暂时不用把 MD5 的数学细节啃得特别深。

但可以知道它的大致过程:

输入数据
   ↓
Padding(填充)
   ↓
切分成 512 bit 数据块
   ↓
初始化 4 个状态寄存器
   ↓
进行 4 轮运算
   ↓
产生 128 bit 摘要
MD5 使用:

A
B
C
D
四个 32 位寄存器。

然后经过多轮:

F
G
H
I
等非线性函数以及:

加法
位运算
循环左移
最终得到:

128 bit
摘要。

所以如果你看到 MD5 源码里面大量的:

&
|
^
<<
>>
不要觉得奇怪,它本质上是在进行大量的位运算。

  


9. 你需要特别区分一个概念

很多初学者会说:

“MD5 加密。”

严格来说不准确。

更准确的说法是:

MD5 哈希

例如:

密码
 ↓
MD5
 ↓
摘要
而不是:

明文
 ↓
加密
 ↓
密文
 ↓
解密
 ↓
明文
这是理解密码学非常重要的一道分水岭。

  


10. 如果你以后做 Go Web 开发

你以后做:

Gin
GORM
MySQL
JWT
Redis
用户登录
很可能会碰到密码存储。

不要这样:

md5.Sum([]byte(password))
然后直接把 MD5 存进 MySQL。

更推荐使用专门用于密码哈希的:

Argon2
bcrypt
scrypt
例如 Go Web 项目里非常常见的:

password
   ↓
bcrypt
   ↓
数据库
登录时:

用户输入密码
      ↓
bcrypt 校验
      ↓
正确 / 错误
而不是尝试“把数据库里的 MD5 解密回来”。

你可以把这一章先记住这 5 句话
MD5 是哈希算法,不是加密算法。

MD5 输出 128 bit,也就是 16 字节。

通常表示成 32 位十六进制字符串。

MD5 已经不适合安全密码学场景。

存用户密码应该使用 bcrypt、scrypt 或 Argon2 等专门的密码哈希算法。
建议下一步把 MD5 → SHA-256 → bcrypt → JWT 这条线串起来学,你会一下子明白 Web 登录系统里的“密码到底是怎么保存和验证的”。

  md 5 加密会破解

package main

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
)
func genMd5(code string)string{
	Md5:=md5.New()
	_,_=io.WriteString(Md5,code)
	return  hex.EncodeToString(Md5.Sum(nil))
}
func main() {
	
	fmt.Println(genMd5("123456"))
}

  md5盐值加密

package main

import (
	"crypto/md5"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"io"
	"strings"
	password "github.com/anaskhan96/go-password-encoder"
)
func genMd5(code string)string{
	Md5:=md5.New()
	_,_=io.WriteString(Md5,code)
	return  hex.EncodeToString(Md5.Sum(nil))
}
func main() {
	
		// Using the default options
		//salt 表示生成的盐值
		//encodedPwd 生成的密码
	// salt, encodedPwd := password.Encode("generic password", nil)
	// fmt.Println(salt)//盐值多少
	// fmt.Println(encodedPwd)//加密后是多少

	// check := password.Verify("generic password", salt, encodedPwd, nil)//去验证
	// fmt.Println(check) // true

	// Using custom options;saltLen 随机盐值长度;Iterations:迭代次数;keyLen:key的长度
	options := &password.Options{SaltLen: 10, Iterations: 100, KeyLen: 32, HashFunction: sha512.New}
	salt, encodedPwd := password.Encode("generic password", options)
	newpassword := fmt.Sprintf("$pbkdf2-sha512$%s$%s",salt,encodedPwd)
	fmt.Println(newpassword)
	passwordInfo:=strings.Split(newpassword,"$")
	fmt.Println(passwordInfo)
	check := password.Verify("generic password", passwordInfo[2], passwordInfo[3], options)
	 fmt.Println(check) // true
	 fmt.Println(len(newpassword))
	
}
启动
PS D:\golang\goproject\src\mxshop_srvs> go run user_srv\model\main\main.go
e10adc3949ba59abbe56e057f20f883e
$pbkdf2-sha512$dnf3UYkXnE$ee418d0b53af0366f82fd56b4484f21d941fb535964efab3f49ab4e15da4460f
[ pbkdf2-sha512 dnf3UYkXnE ee418d0b53af0366f82fd56b4484f21d941fb535964efab3f49ab4e15da4460f]
true
90

  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 (CheckReponse);//检查密码
}
message PasswordCheckInfo {
    string password =1;
    string encryptedPassword=2;

}
message CheckReponse {
    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;

}

  

  生成源码

PS D:\golang\goproject\src\mxshop_srvs\user_srv\proto> protoc --go_out=. --go-grpc_out=. user.proto

      数据库连接

package global

import (
	"log"
	"mxshop_srvs/user_srv/model"
	"os"
	"time"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
)
var (
	DB *gorm.DB
)
func init() {
	dsn := "root:123456@tcp(192.168.40.25: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)
	}
	_ = DB.AutoMigrate(&model.User{})
}

用户服务接口

package handler

import (
	"context"
	"crypto/sha512"
	"fmt"
	"strings"
	"time"

	//"fmt"
	"mxshop_srvs/user_srv/global"
	"mxshop_srvs/user_srv/model"
	"mxshop_srvs/user_srv/proto"

	"github.com/anaskhan96/go-password-encoder"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"gorm.io/gorm"
)
type UserServer struct{
	proto.UnsafeUserServer
}
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 MobileToResponse(User model.User) *proto.UserInfoResPonse{
	//在grpc的message中字段有默认值;不能随便赋值nil进去,容易出错
	//搞清楚哪些字段有默认有默认值
	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 (s *UserServer)GetUserList(cx 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
	}
	rsq := &proto.UserListResPonse{}
	rsq.Total = int32(result.RowsAffected)

	global.DB.Scopes(Paginate(int(req.Pn),int(req.PSize))).Find(&users)
	for _,user:=range users{
		userInfoRsp:=MobileToResponse(user)
		rsq.Data= append(rsq.Data,userInfoRsp)
		
	}
	return rsq,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
	}
	userInfoResPonse := MobileToResponse(user)
	return userInfoResPonse,nil

}
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,"ID不存在")
	}
	if result.Error !=nil{
		return nil,result.Error
	}
	userInfoResPonse := MobileToResponse(user)
	return userInfoResPonse,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)//将查询到的信息填充到user结构体里
	if result.RowsAffected ==1{
		return nil,status.Errorf(codes.AlreadyExists,"用户已存在")
	}
	user.Mobile = req.Mobile
	user.NickName = req.NickName
	//密码加密
	options := &password.Options{SaltLen: 10, Iterations: 100, KeyLen: 32, HashFunction: 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:=MobileToResponse(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.Error(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.Error(codes.Internal,result.Error.Error())
	}
	return  &emptypb.Empty{},nil
}
func (s *UserServer)CheckPassWord(ctx context.Context, req *proto.PasswordCheckInfo) (*proto.CheckReponse, error){
	//校验密码
	passwordInfo:=strings.Split(req.EncryptedPassword,"$")
	// fmt.Println(passwordInfo)
	options := &password.Options{SaltLen: 10, Iterations: 100, KeyLen: 32, HashFunction: sha512.New}
	check := password.Verify(req.Password, passwordInfo[2], passwordInfo[3], options)
	//  fmt.Println(check) // true
	return &proto.CheckReponse{Success: check},nil
}

  grpc 启动

package main

import (
	"flag"
	"fmt"
	"mxshop_srvs/user_srv/handler"
	"mxshop_srvs/user_srv/proto"
	"net"

	"google.golang.org/grpc"
)

func main() {
	IP:=flag.String("ip","0.0.0.0","ip地址")//采用这种方法可以灵活指定IP
	Prot:=flag.Int("prot",50051,"端口:")//采用这种方法可以灵活指定端口
	flag.Parse()
	fmt.Println("IP",*IP)
	fmt.Println("prot",*Prot)

	server := grpc.NewServer()
	proto.RegisterUserServer(server,&handler.UserServer{})
	list,err:= net.Listen("tcp",fmt.Sprintf("%s:%d",*IP,*Prot))//设置监听
	if err!=nil{
		panic("failed to listen"+err.Error())
	}
	err = server.Serve(list)
	if err != nil{
		panic("faile to start grpc:"+err.Error())
	}
}

  打包 测试

PS D:\golang\goproject\src\mxshop_srvs> cd user_srv
PS D:\golang\goproject\src\mxshop_srvs\user_srv> go build main.go
PS D:\golang\goproject\src\mxshop_srvs\user_srv> .\main.exe -h

2026/09/08 10:50:06 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[0.506ms] [rows:-] SELECT DATABASE()

2026/09/08 10:50:06 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[2.257ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'mxshop_user_srv%' ORDER BY SCHEMA_NAME='mxshop_user_srv' DESC,SCHEMA_NAME limit 1

2026/09/08 10:50:06 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[1.203ms] [rows:-] SELECT count(*) FROM information_schema.tables WHERE table_schema = 'mxshop_user_srv' AND table_name = 'user' AND table_type = 'BASE TABLE'

2026/09/08 10:50:06 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[0.589ms] [rows:-] SELECT DATABASE()

2026/09/08 10:50:06 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[1.606ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'mxshop_user_srv%' ORDER BY SCHEMA_NAME='mxshop_user_srv' DESC,SCHEMA_NAME limit 1

2026/09/08 10:50:06 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[2.199ms] [rows:-] SELECT * FROM `user` LIMIT 1

2026/09/08 10:50:06 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[3.116ms] [rows:-] SELECT column_name, column_default, is_nullable = 'YES', data_type, character_maximum_length, column_type, column_key, extra, column_comment, numeric_precision, numeric_scale , datetime_precision FROM information_schema.columns WHERE table_schema = 'mxshop_user_srv' AND table_name = 'user' ORDER BY ORDINAL_POSITION

2026/09/08 10:50:06 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[7.911ms] [rows:0] ALTER TABLE `user` MODIFY COLUMN `gender` varchar(6) comment 'female 表示女,male表示男' DEFAULT 'male'

2026/09/08 10:50:06 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[5.517ms] [rows:0] ALTER TABLE `user` MODIFY COLUMN `role` int comment '1表示普通用户 2表示管理员' DEFAULT 1

2026/09/08 10:50:06 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[1.091ms] [rows:-] SELECT DATABASE()

2026/09/08 10:50:06 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[0.958ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'mxshop_user_srv%' ORDER BY SCHEMA_NAME='mxshop_user_srv' DESC,SCHEMA_NAME limit 1

2026/09/08 10:50:06 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[1.355ms] [rows:-] SELECT count(*) FROM information_schema.statistics WHERE table_schema = 'mxshop_user_srv' AND table_name = 'user' AND index_name = 'idx_mobile'
Usage of D:\golang\goproject\src\mxshop_srvs\user_srv\main.exe:
  -ip string
        ip地址 (default "0.0.0.0")
  -prot int
        端口: (default 50051)
启动
PS D:\golang\goproject\src\mxshop_srvs\user_srv> .\main.exe -prot 50053

2026/09/08 10:51:07 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[0.785ms] [rows:-] SELECT DATABASE()

2026/09/08 10:51:07 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[1.233ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'mxshop_user_srv%' ORDER BY SCHEMA_NAME='mxshop_user_srv' DESC,SCHEMA_NAME limit 1

2026/09/08 10:51:07 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[1.423ms] [rows:-] SELECT count(*) FROM information_schema.tables WHERE table_schema = 'mxshop_user_srv' AND table_name = 'user' AND table_type = 'BASE TABLE'

2026/09/08 10:51:07 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[0.541ms] [rows:-] SELECT DATABASE()

2026/09/08 10:51:07 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[0.562ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'mxshop_user_srv%' ORDER BY SCHEMA_NAME='mxshop_user_srv' DESC,SCHEMA_NAME limit 1

2026/09/08 10:51:07 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[0.525ms] [rows:-] SELECT * FROM `user` LIMIT 1

2026/09/08 10:51:07 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[2.484ms] [rows:-] SELECT column_name, column_default, is_nullable = 'YES', data_type, character_maximum_length, column_type, column_key, extra, column_comment, numeric_precision, numeric_scale , datetime_precision FROM information_schema.columns WHERE table_schema = 'mxshop_user_srv' AND table_name = 'user' ORDER BY ORDINAL_POSITION

2026/09/08 10:51:07 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[7.513ms] [rows:0] ALTER TABLE `user` MODIFY COLUMN `gender` varchar(6) comment 'female 表示女,male表示男' DEFAULT 'male'

2026/09/08 10:51:07 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[6.794ms] [rows:0] ALTER TABLE `user` MODIFY COLUMN `role` int comment '1表示普通用户 2表示管理员' DEFAULT 1

2026/09/08 10:51:07 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[0.520ms] [rows:-] SELECT DATABASE()

2026/09/08 10:51:07 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[1.335ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'mxshop_user_srv%' ORDER BY SCHEMA_NAME='mxshop_user_srv' DESC,SCHEMA_NAME limit 1

2026/09/08 10:51:07 D:/golang/goproject/src/mxshop_srvs/user_srv/global/global.go:37
[1.507ms] [rows:-] SELECT count(*) FROM information_schema.statistics WHERE table_schema = 'mxshop_user_srv' AND table_name = 'user' AND index_name = 'idx_mobile'
IP 0.0.0.0
prot 50053

  生成数据程序

package main

import (
	"crypto/md5"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"mxshop_srvs/user_srv/model"
	"os"

	"time"

	password "github.com/anaskhan96/go-password-encoder"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
)
func genMd5(code string)string{
	Md5:=md5.New()
	_,_=io.WriteString(Md5,code)
	return  hex.EncodeToString(Md5.Sum(nil))
}
func main() {
	dsn := "root:123456@tcp(192.168.40.25: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{})
	fmt.Println(genMd5("123456"))
	options := &password.Options{SaltLen: 10, Iterations: 100, KeyLen: 32, HashFunction: sha512.New}
	salt, encodedPwd := password.Encode("generic password", options)
	newpassword := fmt.Sprintf("$pbkdf2-sha512$%s$%s",salt,encodedPwd)
	fmt.Println(newpassword)
	for i :=0 ;i<10;i++{
		user := model.User{
			NickName: fmt.Sprintf("boobby%d",i),
			Mobile: fmt.Sprintf("1878222222%d",i),
			Password: newpassword,
		}
		db.Save(&user)
	}
}
启动
PS D:\golang\goproject\src\mxshop_srvs> go run user_srv\model\main\main.go

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:59
[0.000ms] [rows:-] SELECT DATABASE()

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:59
[2.078ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'mxshop_user_srv%' ORDER BY SCHEMA_NAME='mxshop_user_srv' DESC,SCHEMA_NAME limit 1

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:59
[2.169ms] [rows:-] SELECT count(*) FROM information_schema.tables WHERE table_schema = 'mxshop_user_srv' AND table_name = 'user' AND table_type = 'BASE TABLE'

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:59
[0.000ms] [rows:-] SELECT DATABASE()

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:59
[3.469ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'mxshop_user_srv%' ORDER BY SCHEMA_NAME='mxshop_user_srv' DESC,SCHEMA_NAME limit 1

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:59
[1.554ms] [rows:-] SELECT * FROM `user` LIMIT 1

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:59
[2.861ms] [rows:-] SELECT column_name, column_default, is_nullable = 'YES', data_type, character_maximum_length, column_type, column_key, extra, column_comment, numeric_precision, numeric_scale , datetime_precision FROM information_schema.columns WHERE table_schema = 'mxshop_user_srv' AND table_name = 'user' ORDER BY ORDINAL_POSITION

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:59
[14.584ms] [rows:0] ALTER TABLE `user` MODIFY COLUMN `gender` varchar(6) comment 'female 表示女,male表示男' DEFAULT 'male'

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:59
[8.022ms] [rows:0] ALTER TABLE `user` MODIFY COLUMN `role` int comment '1表示普通用户 2表示管理员' DEFAULT 1

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:59
[1.528ms] [rows:-] SELECT DATABASE()

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:59
[1.654ms] [rows:1] SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE 'mxshop_user_srv%' ORDER BY SCHEMA_NAME='mxshop_user_srv' DESC,SCHEMA_NAME limit 1

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:59
[1.574ms] [rows:-] SELECT count(*) FROM information_schema.statistics WHERE table_schema = 'mxshop_user_srv' AND table_name = 'user' AND index_name = 'idx_mobile'
e10adc3949ba59abbe56e057f20f883e
$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:89
[6.989ms] [rows:1] INSERT INTO `user` (`add_time`,`Update_time`,`deleted_at`,`is_deleted`,`mobile`,`password`,`nick_name`,`birthday`,`gender`,`role`) VALUES ('2026-09-08 12:26:09.102','2026-09-08 12:26:09.102',NULL,false,'18782222220','$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c','boobby0',NULL,'male',1)

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:89
[8.204ms] [rows:1] INSERT INTO `user` (`add_time`,`Update_time`,`deleted_at`,`is_deleted`,`mobile`,`password`,`nick_name`,`birthday`,`gender`,`role`) VALUES ('2026-09-08 12:26:09.109','2026-09-08 12:26:09.109',NULL,false,'18782222221','$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c','boobby1',NULL,'male',1)

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:89
[5.083ms] [rows:1] INSERT INTO `user` (`add_time`,`Update_time`,`deleted_at`,`is_deleted`,`mobile`,`password`,`nick_name`,`birthday`,`gender`,`role`) VALUES ('2026-09-08 12:26:09.117','2026-09-08 12:26:09.117',NULL,false,'18782222222','$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c','boobby2',NULL,'male',1)

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:89
[6.636ms] [rows:1] INSERT INTO `user` (`add_time`,`Update_time`,`deleted_at`,`is_deleted`,`mobile`,`password`,`nick_name`,`birthday`,`gender`,`role`) VALUES ('2026-09-08 12:26:09.123','2026-09-08 12:26:09.123',NULL,false,'18782222223','$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c','boobby3',NULL,'male',1)

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:89
[5.491ms] [rows:1] INSERT INTO `user` (`add_time`,`Update_time`,`deleted_at`,`is_deleted`,`mobile`,`password`,`nick_name`,`birthday`,`gender`,`role`) VALUES ('2026-09-08 12:26:09.13','2026-09-08 12:26:09.13',NULL,false,'18782222224','$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c','boobby4',NULL,'male',1)

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:89
[6.087ms] [rows:1] INSERT INTO `user` (`add_time`,`Update_time`,`deleted_at`,`is_deleted`,`mobile`,`password`,`nick_name`,`birthday`,`gender`,`role`) VALUES ('2026-09-08 12:26:09.136','2026-09-08 12:26:09.136',NULL,false,'18782222225','$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c','boobby5',NULL,'male',1)

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:89
[8.296ms] [rows:1] INSERT INTO `user` (`add_time`,`Update_time`,`deleted_at`,`is_deleted`,`mobile`,`password`,`nick_name`,`birthday`,`gender`,`role`) VALUES ('2026-09-08 12:26:09.143','2026-09-08 12:26:09.143',NULL,false,'18782222226','$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c','boobby6',NULL,'male',1)

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:89
[5.144ms] [rows:1] INSERT INTO `user` (`add_time`,`Update_time`,`deleted_at`,`is_deleted`,`mobile`,`password`,`nick_name`,`birthday`,`gender`,`role`) VALUES ('2026-09-08 12:26:09.151','2026-09-08 12:26:09.151',NULL,false,'18782222227','$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c','boobby7',NULL,'male',1)

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:89
[8.294ms] [rows:1] INSERT INTO `user` (`add_time`,`Update_time`,`deleted_at`,`is_deleted`,`mobile`,`password`,`nick_name`,`birthday`,`gender`,`role`) VALUES ('2026-09-08 12:26:09.157','2026-09-08 12:26:09.157',NULL,false,'18782222228','$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c','boobby8',NULL,'male',1)

2026/09/08 12:26:09 D:/golang/goproject/src/mxshop_srvs/user_srv/model/main/main.go:89
[6.766ms] [rows:1] INSERT INTO `user` (`add_time`,`Update_time`,`deleted_at`,`is_deleted`,`mobile`,`password`,`nick_name`,`birthday`,`gender`,`role`) VALUES ('2026-09-08 12:26:09.165','2026-09-08 12:26:09.165',NULL,false,'18782222229','$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c','boobby9',NULL,'male',1)

  编写测试文件检测用户密码

package main

import (
	"context"
	"fmt"
	"mxshop_srvs/user_srv/proto"

	//"os/user"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)
var userClient proto.UserClient
var conn *grpc.ClientConn
func Init(){
	var err error
	conn, err = grpc.NewClient(
    "127.0.0.1:50053",
    grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err !=nil{
        panic("连接失败")
    }
    
	userClient=proto.NewUserClient(conn)

}

func TestGetUserList() {
	res,err:=userClient.GetUserList(context.Background(),&proto.PageInfo{
		Pn: 1,
		PSize: 2,
	})
	if err != nil{
		panic(err)
	}
	for _,user:=range res.Data{
		fmt.Printf(user.Mobile,user.NickName,user.Password)
		checkRes,err := userClient.CheckPassWord(context.Background(),&proto.PasswordCheckInfo{
			Password: "generic password",
			EncryptedPassword: user.Password,
		})
		if err !=nil{
			panic(err)
		}
		fmt.Println(checkRes.Success)
	}


}

func main(){
	Init()
	TestGetUserList()
	defer conn.Close()
}
启动
PS D:\golang\goproject\src\mxshop_srvs> go run user_srv\tests\user.go
%!(EXTRA string=boobby0, string=$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c)true
%!(EXTRA string=boobby1, string=$pbkdf2-sha512$jjD3MnS37n$5f4287c312380fe4b89a741a495954eae303b6a1b033665c849e3cdf0d96382c)true

  创建用户测试

package main

import (
	"context"
	"fmt"
	"mxshop_srvs/user_srv/proto"

	//"os/user"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)
var userClient proto.UserClient
var conn *grpc.ClientConn
func Init(){
	var err error
	conn, err = grpc.NewClient(
    "127.0.0.1:50053",
    grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err !=nil{
        panic("连接失败")
    }
    
	userClient=proto.NewUserClient(conn)

}

func TestGetUserList() {
	res,err:=userClient.GetUserList(context.Background(),&proto.PageInfo{
		Pn: 1,
		PSize: 2,
	})
	if err != nil{
		panic(err)
	}
	for _,user:=range res.Data{
		fmt.Printf(user.Mobile,user.NickName,user.Password)
		checkRes,err := userClient.CheckPassWord(context.Background(),&proto.PasswordCheckInfo{
			Password: "generic password",
			EncryptedPassword: user.Password,
		})
		if err !=nil{
			panic(err)
		}
		fmt.Println(checkRes.Success)
	}


}
func TestCreateUser(){
	for i := 10;i<20;i++{
		rsp,err:= userClient.CreateUser(context.Background(),&proto.CreateUserInfo{
			NickName: fmt.Sprintf("bobby%d",i),
			Mobile: fmt.Sprintf("187822222%d",i),
			PassWord: "admin123",
		})
		if err != nil{
			panic(err)
		}
		fmt.Println(rsp.Id)
	}
}
func main(){
	Init()
	//TestGetUserList()
	TestCreateUser()
	defer conn.Close()
}

启动
PS D:\golang\goproject\src\mxshop_srvs> go run user_srv\tests\user.go
11
12
13
14
15
16
17
18
19
20

  

  

 

posted @ 2026-09-08 12:38  烟雨楼台,行云流水  阅读(6)  评论(0)    收藏  举报