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 查看数据表

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
草都可以从石头缝隙中长出来更可况你呢

浙公网安备 33010602011771号