Go-kratos 框架商城微服务实战之用户服务 (四)
这篇主要编写 HTTP API 端的服务,跟前几篇写的用户服务对接上,主要还是项目的初始化准备工作。写的不清晰的地方可看GitHub 源码 , 也感谢您指出不足之处。
注:竖排 … 代码省略,为了保持文章的篇幅简洁,我会将一些不必要的代码使用竖排的 . 来代替,你在复制本文代码块的时候,切记不要将 . 也一同复制进去。这里所有 import 引入的包都没有特殊说明,自己写的时候要注意包的引入。
shop Api 准备工作
new 一个新的 kratos 项目
在 kratos-shop 目录下新建一个 kratos new shop
shop 项目
// 整体的项目 目录结构如下 | |
|-- kratos-shop | |
|-- service | |
|-- user // 原先的用户服务 grpc | |
|-- shop // 刚刚通过 kratos new shop 新增的项目代码 |
- 进入新建的 shop 目录下
- 删除目录下的所有文件
rm -rf api/helloworld
- 复制
service/user/api/user/v1/user.proto
文件到新建的shop/api/service/user/v1
目录下 - 执行命令
kratos proto add api/shop/v1/user.proto
创建 api user.proto - 执行
kratos proto server api/user/v1/user.proto -t internal/service
命令生成对应的 service 文件。 - 删除不需要的 service 文件
rm internal/service/greeter.go
- 完整执行命令如下:
cd kratos-shop | |
kratos new shop | |
cd shop | |
rm -rf api/helloworld | |
rm internal/service/greeter.go | |
kratos proto add api/shop/v1/user.proto | |
kratos proto server api/shop/v1/user.proto -t internal/service | |
mkdir -p api/service/user/v1 | |
cp ../service/user/api/user/v1/user.proto api/service/user/v1 |
proto 的目录结构如下:
├── api | |
│ ├── service | |
│ │ └── user | |
│ │ └── v1 | |
│ │ └── user.proto | |
│ └── shop | |
│ └── v1 | |
│ └── user.proto |
修改 shop 下的 user.proto
这里提供对外访问的接口,会聚合从不同的服务之间获取数据,接口路由通过 Protobuf IDL 定义对应的 REST API 和 gRPC API,
参数校验使用 Validate 中间件,使用 proto-gen-validate 生成
在使用 validate 之前首先需要安装 proto-gen-validate。
syntax = "proto3"; | |
package shop.shop.v1; | |
// 这里可以把 proto 文件下载下来,放到项目的 third_party 目录下 | |
import "google/api/annotations.proto"; | |
import "google/protobuf/empty.proto"; | |
import "validate/validate.proto"; | |
option go_package = "shop/api/shop/v1;v1"; | |
// The Shop service definition. | |
service Shop { | |
rpc Register (RegisterReq) returns (RegisterReply) { | |
option (google.api.http) = { | |
post: "/api/users/register", | |
body: "*", | |
}; | |
} | |
rpc Login (LoginReq) returns (RegisterReply) { | |
option (google.api.http) = { | |
post: "/api/users/login", | |
body: "*", | |
}; | |
} | |
rpc Captcha (google.protobuf.Empty) returns (CaptchaReply) { | |
option (google.api.http) = { | |
get: "/api/users/captcha", | |
}; | |
} | |
rpc Detail (google.protobuf.Empty) returns (UserDetailResponse) { | |
option (google.api.http) = { | |
get: "/api/users/detail", | |
}; | |
} | |
} | |
// Data returned by registration and login | |
message RegisterReply { | |
int64 id = 1; | |
string mobile = 3; | |
string username = 4; | |
string token = 5; | |
int64 expiredAt = 6; | |
} | |
message RegisterReq { | |
string mobile = 1 [(validate.rules).string.len = 11]; | |
string username = 2 [(validate.rules).string = {min_len: 3, max_len: 15}]; | |
string password = 3 [(validate.rules).string = {min_len: 8}]; | |
} | |
message LoginReq { | |
string mobile = 1 [(validate.rules).string.len = 11]; | |
string password = 2 [(validate.rules).string = {min_len: 8}]; | |
string captcha = 3 [(validate.rules).string = {min_len: 5,max_len:5}]; | |
string captchaId = 4 [(validate.rules).string ={min_len: 1}]; | |
} | |
// user Detail returned | |
message UserDetailResponse{ | |
int64 id = 1; | |
string mobile = 2; | |
string nickName = 3; | |
int64 birthday = 4; | |
string gender = 5; | |
int32 role = 6; | |
} | |
message CaptchaReply{ | |
string captchaId = 1; | |
string picPath = 2; | |
} |
这里一共定义了 4 个 rpc 方法,其中三个是需要跟之前写的用户服务交互的,还有个获取图片验证码的接口,自己内部实现。
- shop 根目录执行
make api
,生成对应的*pb.go
文件
修改配置文件
- 修改
shop/configs/config.yaml
文件
项目中引入了 consul 配置需要把相关的配置设置好,service 就是 consul 用来服务发现的。这里的 trace 并没有用到呢先在这里定义了,之后会专门拿一篇来说说。auth 是用来 jwt 验证。
name: shop.api | |
server: | |
http: | |
addr: 0.0.0.0:8097 | |
timeout: 1s | |
grpc: | |
addr: 0.0.0.0:9001 | |
timeout: 1s | |
data: | |
database: | |
driver: mysql | |
source: root:root@tcp(127.0.0.1:3306)/test | |
redis: | |
addr: 127.0.0.1:6379 | |
read_timeout: 0.2s | |
write_timeout: 0.2s | |
trace: | |
endpoint: http://127.0.0.1:14268/api/traces | |
auth: | |
jwt_key: hqFr%3ddt32DGlSTOI5cO6@TH#fFwYnP$S | |
service: | |
user: | |
endpoint: discovery:///shop.user.service | |
goods: | |
endpoint: discovery:///shop.goods.service |
- 新增
shop/configs/registry.yaml
文件
consul: | |
address: 127.0.0.1:8500 | |
scheme: http |
- 修改
internal/conf/conf.proto
文件:
syntax = "proto3"; | |
package shop.api; | |
option go_package = "shop/internal/conf;conf"; | |
import "google/protobuf/duration.proto"; | |
message Bootstrap { | |
Server server = 1; | |
Data data = 2; | |
Trace trace = 3; // 链路追踪 | |
Auth auth = 4; // 认证鉴权 | |
Service service = 5; // 服务注册与发现 | |
} | |
message Server { | |
message HTTP { | |
string network = 1; | |
string addr = 2; | |
google.protobuf.Duration timeout = 3; | |
} | |
message GRPC { | |
string network = 1; | |
string addr = 2; | |
google.protobuf.Duration timeout = 3; | |
} | |
HTTP http = 1; | |
GRPC grpc = 2; | |
} | |
message Data { | |
message Database { | |
string driver = 1; | |
string source = 2; | |
} | |
message Redis { | |
string network = 1; | |
string addr = 2; | |
google.protobuf.Duration read_timeout = 3; | |
google.protobuf.Duration write_timeout = 4; | |
} | |
Database database = 1; | |
Redis redis = 2; | |
} | |
message Service { | |
message User { // 用户服务 | |
string endpoint = 1; | |
} | |
message Goods { // 商品服务 | |
string endpoint = 1; | |
} | |
User user = 1; | |
Goods goods = 2; | |
} | |
message Trace { | |
string endpoint = 1; | |
} | |
message Registry { | |
message Consul { | |
string address = 1; | |
string scheme = 2; | |
} | |
Consul consul = 1; | |
} | |
message Auth { | |
string jwt_key = 1; | |
} |
- 生成新的配置文件
shop 根目录执行 | |
make config | |
生成 shop/internal/conf/conf.pb.go 文件 |
修改 HTTP 服务
- 修改
internal/server/http.go
文件
这里用到的一些 middleware 中间件都是 kratos 官方支持的,jwt、validate、tracing,具体使用方式可参考 kratos 的middleware 文档
package server | |
import ( | |
"context" | |
"github.com/go-kratos/kratos/v2/log" | |
"github.com/go-kratos/kratos/v2/middleware/auth/jwt" | |
"github.com/go-kratos/kratos/v2/middleware/logging" | |
"github.com/go-kratos/kratos/v2/middleware/recovery" | |
"github.com/go-kratos/kratos/v2/middleware/selector" | |
"github.com/go-kratos/kratos/v2/middleware/validate" | |
"github.com/go-kratos/kratos/v2/transport/http" | |
jwt2 "github.com/golang-jwt/jwt/v4" | |
"github.com/gorilla/handlers" | |
v1 "shop/api/shop/v1" | |
"shop/internal/conf" | |
"shop/internal/service" | |
) | |
// NewHTTPServer new an HTTP server. | |
func NewHTTPServer(c *conf.Server, ac *conf.Auth, s *service.ShopService, logger log.Logger) *http.Server { | |
var opts = []http.ServerOption{ | |
http.Middleware( | |
recovery.Recovery(), | |
validate.Validator(), // 接口访问的参数校验 | |
selector.Server( // jwt 验证 | |
jwt.Server(func(token *jwt2.Token) (interface{}, error) { | |
return []byte(ac.JwtKey), nil | |
}, jwt.WithSigningMethod(jwt2.SigningMethodHS256)), | |
).Match(NewWhiteListMatcher()).Build(), | |
logging.Server(logger), | |
), | |
http.Filter(handlers.CORS( // 浏览器跨域 | |
handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}), | |
handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS"}), | |
handlers.AllowedOrigins([]string{"*"}), | |
)), | |
} | |
if c.Http.Network != "" { | |
opts = append(opts, http.Network(c.Http.Network)) | |
} | |
if c.Http.Addr != "" { | |
opts = append(opts, http.Address(c.Http.Addr)) | |
} | |
if c.Http.Timeout != nil { | |
opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration())) | |
} | |
srv := http.NewServer(opts...) | |
v1.RegisterShopHTTPServer(srv, s) | |
return srv | |
} | |
// NewWhiteListMatcher 设置白名单,不需要 token 验证的接口 | |
func NewWhiteListMatcher() selector.MatchFunc { | |
whiteList := make(map[string]struct{}) | |
whiteList["/shop.shop.v1.Shop/Captcha"] = struct{}{} | |
whiteList["/shop.shop.v1.Shop/Login"] = struct{}{} | |
whiteList["/shop.shop.v1.Shop/Register"] = struct{}{} | |
return func(ctx context.Context, operation string) bool { | |
if _, ok := whiteList[operation]; ok { | |
return false | |
} | |
return true | |
} | |
} |
- 修改
internal/server/server.go
由于此服务只对外提供 http 服务,所以同目录下 grpc 文件可以删除,这样子注册服务的时候也把 grpc 服务去掉。
package server | |
import ( | |
"github.com/google/wire" | |
) | |
// ProviderSet is server providers. | |
var ProviderSet = wire.NewSet(NewHTTPServer) |
实现接口
- 修改
shop/internal/service/service.go
文件
package service | |
import ( | |
"github.com/go-kratos/kratos/v2/log" | |
"github.com/google/wire" | |
v1 "shop/api/shop/v1" | |
"shop/internal/biz" | |
) | |
// ProviderSet is service providers. | |
var ProviderSet = wire.NewSet(NewShopService) | |
// ShopService is a shop service. | |
type ShopService struct { | |
v1.UnimplementedShopServer | |
uc *biz.UserUsecase | |
log *log.Helper | |
} | |
// NewShopService new a shop service. | |
func NewShopService(uc *biz.UserUsecase, logger log.Logger) *ShopService { | |
return &ShopService{ | |
uc: uc, | |
log: log.NewHelper(log.With(logger, "module", "service/shop")), | |
} | |
} |
- 修改
shop/internal/service/user.go
这里的 ShopService 的 usecase 还没实现,编辑器可能会有错误提示,先忽略
package service | |
import ( | |
"context" | |
"google.golang.org/protobuf/types/known/emptypb" | |
v1 "shop/api/shop/v1" | |
) | |
func (s *ShopService) Register(ctx context.Context, req *v1.RegisterReq) (*v1.RegisterReply, error) { | |
return s.uc.CreateUser(ctx, req) | |
} | |
func (s *ShopService) Login(ctx context.Context, req *v1.LoginReq) (*v1.RegisterReply, error) { | |
return s.uc.PassWordLogin(ctx, req) | |
} | |
func (s *ShopService) Captcha(ctx context.Context, r *emptypb.Empty) (*v1.CaptchaReply, error) { | |
return s.uc.GetCaptcha(ctx) | |
} | |
func (s *ShopService) Detail(ctx context.Context, r *emptypb.Empty) (*v1.UserDetailResponse, error) { | |
return s.uc.UserDetailByID(ctx) | |
} |
新增 jwt 验证的 Middleware
- 新建文件
internal/pkg/middleware/auth/auth.go
package auth | |
import ( | |
"errors" | |
"github.com/golang-jwt/jwt/v4" | |
) | |
type CustomClaims struct { | |
ID int64 | |
NickName string | |
AuthorityId int | |
jwt.StandardClaims | |
} | |
// CreateToken generate token | |
func CreateToken(c CustomClaims, key string) (string, error) { | |
claims := jwt.NewWithClaims(jwt.SigningMethodHS256, c) | |
signedString, err := claims.SignedString([]byte(key)) | |
if err != nil { | |
return "", errors.New("generate token failed" + err.Error()) | |
} | |
return signedString, nil | |
} |
新增生成验证码的文件
- 新建文件
internal/pkg/captcha/captcha.go
package captcha | |
import ( | |
"context" | |
"github.com/mojocn/base64Captcha" | |
) | |
var Store = base64Captcha.DefaultMemStore | |
type CaptchaInfo struct { | |
CaptchaId string | |
PicPath string | |
} | |
// GetCaptcha 生成验证码 | |
func GetCaptcha(ctx context.Context) (*CaptchaInfo, error) { | |
driver := base64Captcha.NewDriverDigit(80, 250, 5, 0.7, 80) | |
cp := base64Captcha.NewCaptcha(driver, Store) | |
id, b64s, err := cp.Generate() | |
if err != nil { | |
return nil, err | |
} | |
return &CaptchaInfo{ | |
CaptchaId: id, | |
PicPath: b64s, | |
}, nil | |
} |
- 修改
shop/internal/biz/user.go
package biz | |
import ( | |
"context" | |
"errors" | |
"github.com/go-kratos/kratos/v2/log" | |
"github.com/go-kratos/kratos/v2/middleware/auth/jwt" | |
jwt2 "github.com/golang-jwt/jwt/v4" | |
v1 "shop/api/shop/v1" | |
"shop/internal/conf" | |
"shop/internal/pkg/captcha" | |
"shop/internal/pkg/middleware/auth" | |
"time" | |
) | |
// 定义错误信息 | |
var ( | |
ErrPasswordInvalid = errors.New("password invalid") | |
ErrUsernameInvalid = errors.New("username invalid") | |
ErrCaptchaInvalid = errors.New("verification code error") | |
ErrMobileInvalid = errors.New("mobile invalid") | |
ErrUserNotFound = errors.New("user not found") | |
ErrLoginFailed = errors.New("login failed") | |
ErrGenerateTokenFailed = errors.New("generate token failed") | |
ErrAuthFailed = errors.New("authentication failed") | |
) | |
// 定义返回的数据的结构体 | |
type User struct { | |
ID int64 | |
Mobile string | |
NickName string | |
Birthday int64 | |
Gender string | |
Role int | |
CreatedAt time.Time | |
} | |
type UserRepo interface { | |
CreateUser(c context.Context, u *User) (*User, error) | |
UserByMobile(ctx context.Context, mobile string) (*User, error) | |
UserById(ctx context.Context, Id int64) (*User, error) | |
CheckPassword(ctx context.Context, password, encryptedPassword string) (bool, error) | |
} | |
type UserUsecase struct { | |
uRepo UserRepo | |
log *log.Helper | |
signingKey string // 这里是为了生存 token 的时候可以直接取配置文件里面的配置 | |
} | |
func NewUserUsecase(repo UserRepo, logger log.Logger, conf *conf.Auth) *UserUsecase { | |
helper := log.NewHelper(log.With(logger, "module", "usecase/shop")) | |
return &UserUsecase{uRepo: repo, log: helper, signingKey: conf.JwtKey} | |
} | |
// GetCaptcha 验证码 | |
func (uc *UserUsecase) GetCaptcha(ctx context.Context) (*v1.CaptchaReply, error) { | |
captchaInfo, err := captcha.GetCaptcha(ctx) | |
if err != nil { | |
return nil, err | |
} | |
return &v1.CaptchaReply{ | |
CaptchaId: captchaInfo.CaptchaId, | |
PicPath: captchaInfo.PicPath, | |
}, nil | |
} | |
func (uc *UserUsecase) UserDetailByID(ctx context.Context) (*v1.UserDetailResponse, error) { | |
// 在上下文 context 中取出 claims 对象 | |
var uId int64 | |
if claims, ok := jwt.FromContext(ctx); ok { | |
c := claims.(jwt2.MapClaims) | |
if c["ID"] == nil { | |
return nil, ErrAuthFailed | |
} | |
uId = int64(c["ID"].(float64)) | |
} | |
user, err := uc.uRepo.UserById(ctx, uId) | |
if err != nil { | |
return nil, err | |
} | |
return &v1.UserDetailResponse{ | |
Id: user.ID, | |
NickName: user.NickName, | |
Mobile: user.Mobile, | |
}, nil | |
} | |
func (uc *UserUsecase) PassWordLogin(ctx context.Context, req *v1.LoginReq) (*v1.RegisterReply, error) { | |
// 表单验证 | |
if len(req.Mobile) <= 0 { | |
return nil, ErrMobileInvalid | |
} | |
if len(req.Password) <= 0 { | |
return nil, ErrUsernameInvalid | |
} | |
// 验证验证码是否正确 | |
if !captcha.Store.Verify(req.CaptchaId, req.Captcha, true) { | |
return nil, ErrCaptchaInvalid | |
} | |
if user, err := uc.uRepo.UserByMobile(ctx, req.Mobile); err != nil { | |
return nil, ErrUserNotFound | |
} else { | |
// 用户存在检查密码 | |
if passRsp, pasErr := uc.uRepo.CheckPassword(ctx, req.Password, user.Password); pasErr != nil { | |
return nil, ErrPasswordInvalid | |
} else { | |
if passRsp { | |
claims := auth.CustomClaims{ | |
ID: user.ID, | |
NickName: user.NickName, | |
AuthorityId: user.Role, | |
StandardClaims: jwt2.StandardClaims{ | |
NotBefore: time.Now().Unix(), // 签名的生效时间 | |
ExpiresAt: time.Now().Unix() + 60*60*24*30, // 30天过期 | |
Issuer: "Gyl", | |
}, | |
} | |
token, err := auth.CreateToken(claims, uc.signingKey) | |
if err != nil { | |
return nil, ErrGenerateTokenFailed | |
} | |
return &v1.RegisterReply{ | |
Id: user.ID, | |
Mobile: user.Mobile, | |
Username: user.NickName, | |
Token: token, | |
ExpiredAt: time.Now().Unix() + 60*60*24*30, | |
}, nil | |
} else { | |
return nil, ErrLoginFailed | |
} | |
} | |
} | |
} | |
func (uc *UserUsecase) CreateUser(ctx context.Context, req *v1.RegisterReq) (*v1.RegisterReply, error) { | |
newUser, err := NewUser(req.Mobile, req.Username, req.Password) | |
if err != nil { | |
return nil, err | |
} | |
createUser, err := uc.uRepo.CreateUser(ctx, &newUser) | |
if err != nil { | |
return nil, err | |
} | |
claims := auth.CustomClaims{ | |
ID: createUser.ID, | |
NickName: createUser.NickName, | |
AuthorityId: createUser.Role, | |
StandardClaims: jwt2.StandardClaims{ | |
NotBefore: time.Now().Unix(), // 签名的生效时间 | |
ExpiresAt: time.Now().Unix() + 60*60*24*30, // 30天过期 | |
Issuer: "Gyl", | |
}, | |
} | |
token, err := auth.CreateToken(claims, uc.signingKey) | |
if err != nil { | |
return nil, err | |
} | |
return &v1.RegisterReply{ | |
Id: createUser.ID, | |
Mobile: createUser.Mobile, | |
Username: createUser.NickName, | |
Token: token, | |
ExpiredAt: time.Now().Unix() + 60*60*24*30, | |
}, nil | |
} | |
func NewUser(mobile, username, password string) (User, error) { | |
// check mobile | |
if len(mobile) <= 0 { | |
return User{}, ErrMobileInvalid | |
} | |
// check username | |
if len(username) <= 0 { | |
return User{}, ErrUsernameInvalid | |
} | |
// check password | |
if len(password) <= 0 { | |
return User{}, ErrPasswordInvalid | |
} | |
return User{ | |
Mobile: mobile, | |
NickName: username, | |
Password: password, | |
}, nil | |
} |
- 修改
shop/internal/biz/biz.go
文件
package biz | |
import "github.com/google/wire" | |
// ProviderSet is biz providers. | |
var ProviderSet = wire.NewSet(NewUserUsecase) |
- 修改
internal/data/data.go
这里比较重要,data 不是直接链接本机器配置的数据库的,而是链接各个服务,通过服务提供的 rpc 接口去获取数据的。
package data | |
import ( | |
"context" | |
consul "github.com/go-kratos/kratos/contrib/registry/consul/v2" | |
"github.com/go-kratos/kratos/v2/log" | |
"github.com/go-kratos/kratos/v2/middleware/recovery" | |
"github.com/go-kratos/kratos/v2/registry" | |
"github.com/go-kratos/kratos/v2/transport/grpc" | |
"github.com/google/wire" | |
consulAPI "github.com/hashicorp/consul/api" | |
grpcx "google.golang.org/grpc" | |
userV1 "shop/api/service/user/v1" | |
"shop/internal/conf" | |
"time" | |
) | |
// ProviderSet is data providers. | |
var ProviderSet = wire.NewSet(NewData, NewUserRepo, NewUserServiceClient, NewRegistrar, NewDiscovery) | |
// Data . | |
type Data struct { | |
log *log.Helper | |
uc userV1.UserClient // 用户服务的客户端 | |
} | |
// NewData . | |
func NewData(c *conf.Data, uc userV1.UserClient, logger log.Logger) (*Data, error) { | |
l := log.NewHelper(log.With(logger, "module", "data")) | |
return &Data{log: l, uc: uc}, nil | |
} | |
// NewUserServiceClient 链接用户服务 | |
func NewUserServiceClient(ac *conf.Auth, sr *conf.Service, rr registry.Discovery) userV1.UserClient { | |
conn, err := grpc.DialInsecure( | |
context.Background(), | |
grpc.WithEndpoint(sr.User.Endpoint),// consul | |
grpc.WithDiscovery(rr),// consul | |
grpc.WithMiddleware( | |
recovery.Recovery(), | |
), | |
grpc.WithTimeout(2*time.Second), | |
) | |
if err != nil { | |
panic(err) | |
} | |
c := userV1.NewUserClient(conn) | |
return c | |
} | |
// NewRegistrar add consul | |
func NewRegistrar(conf *conf.Registry) registry.Registrar { | |
c := consulAPI.DefaultConfig() | |
c.Address = conf.Consul.Address | |
c.Scheme = conf.Consul.Scheme | |
cli, err := consulAPI.NewClient(c) | |
if err != nil { | |
panic(err) | |
} | |
r := consul.New(cli, consul.WithHealthCheck(false)) | |
return r | |
} | |
func NewDiscovery(conf *conf.Registry) registry.Discovery { | |
c := consulAPI.DefaultConfig() | |
c.Address = conf.Consul.Address | |
c.Scheme = conf.Consul.Scheme | |
cli, err := consulAPI.NewClient(c) | |
if err != nil { | |
panic(err) | |
} | |
r := consul.New(cli, consul.WithHealthCheck(false)) | |
return r | |
} |
- 修改
shop/internal/data/user.go
package data | |
import ( | |
"context" | |
"github.com/go-kratos/kratos/v2/log" | |
userService "shop/api/service/user/v1" | |
"shop/internal/biz" | |
) | |
type userRepo struct { | |
data *Data | |
log *log.Helper | |
} | |
// NewUserRepo . | |
func NewUserRepo(data *Data, logger log.Logger) biz.UserRepo { | |
return &userRepo{ | |
data: data, | |
log: log.NewHelper(log.With(logger, "module", "repo/user")), | |
} | |
} | |
func (u *userRepo) CreateUser(c context.Context, user *biz.User) (*biz.User, error) { | |
createUser, err := u.data.uc.CreateUser(c, &userService.CreateUserInfo{ | |
NickName: user.NickName, | |
Password: user.Password, | |
Mobile: user.Mobile, | |
}) | |
if err != nil { | |
return nil, err | |
} | |
return &biz.User{ | |
ID: createUser.Id, | |
Mobile: createUser.Mobile, | |
NickName: createUser.NickName, | |
}, nil | |
} | |
func (u *userRepo) UserByMobile(c context.Context, mobile string) (*biz.User, error) { | |
byMobile, err := u.data.uc.GetUserByMobile(c, &userService.MobileRequest{Mobile: mobile}) | |
if err != nil { | |
return nil, err | |
} | |
return &biz.User{ | |
Mobile: byMobile.Mobile, | |
ID: byMobile.Id, | |
NickName: byMobile.NickName, | |
}, nil | |
} | |
func (u *userRepo) CheckPassword(c context.Context, password, encryptedPassword string) (bool, error) { | |
if byMobile, err := u.data.uc.CheckPassword(c, &userService.PasswordCheckInfo{Password: password, EncryptedPassword: encryptedPassword}); err != nil { | |
return false, err | |
} else { | |
return byMobile.Success, nil | |
} | |
} | |
func (u *userRepo) UserById(c context.Context, id int64) (*biz.User, error) { | |
user, err := u.data.uc.GetUserById(c, &userService.IdRequest{Id: id}) | |
if err != nil { | |
return nil, err | |
} | |
return &biz.User{ | |
ID: user.Id, | |
Mobile: user.Mobile, | |
NickName: user.NickName, | |
Gender: user.Gender, | |
Role: int(user.Role), | |
}, nil | |
} |
修改启动服务
- 修改
wire.go
一定要注意这里的注入的参数,多了少了都会报错的
package main | |
... | |
func initApp(*conf.Server, *conf.Data, *conf.Auth, *conf.Service, *conf.Registry, log.Logger) (*kratos.App, func(), error) { | |
panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp)) | |
} |
- 重新生成依赖注入关系
根目录执行 | |
make wire |
- 修改入口文件
main.go
package main | |
import ( | |
"flag" | |
"os" | |
"github.com/go-kratos/kratos/v2" | |
"github.com/go-kratos/kratos/v2/config" | |
"github.com/go-kratos/kratos/v2/config/file" | |
"github.com/go-kratos/kratos/v2/log" | |
"github.com/go-kratos/kratos/v2/registry" | |
"github.com/go-kratos/kratos/v2/transport/grpc" | |
"github.com/go-kratos/kratos/v2/transport/http" | |
"shop/internal/conf" | |
) | |
// go build -ldflags "-X main.Version=x.y.z" | |
var ( | |
// Name is the name of the compiled software. | |
Name = "shop.api" | |
// Version is the version of the compiled software. | |
Version = "shop.api.v1" | |
// flagconf is the config flag. | |
flagconf string | |
id, _ = os.Hostname() | |
) | |
func init() { | |
flag.StringVar(&flagconf, "conf", "../../configs", "config path, eg: -conf config.yaml") | |
} | |
func newApp(logger log.Logger, hs *http.Server, gs *grpc.Server, rr registry.Registrar) *kratos.App { | |
return kratos.New( | |
kratos.ID(id+"shop.api"), | |
kratos.Name(Name), | |
kratos.Version(Version), | |
kratos.Metadata(map[string]string{}), | |
kratos.Logger(logger), | |
kratos.Server( | |
hs, | |
), | |
kratos.Registrar(rr), | |
) | |
} | |
func main() { | |
flag.Parse() | |
logger := log.With(log.NewStdLogger(os.Stdout), | |
"ts", log.DefaultTimestamp, | |
"caller", log.DefaultCaller, | |
"service.id", id, | |
"service.name", Name, | |
"service.version", Version, | |
"trace_id", tracing.TraceID(), | |
"span_id", tracing.SpanID(), | |
) | |
c := config.New( | |
config.WithSource( | |
file.NewSource(flagconf), | |
), | |
) | |
defer c.Close() | |
if err := c.Load(); err != nil { | |
panic(err) | |
} | |
var bc conf.Bootstrap | |
if err := c.Scan(&bc); err != nil { | |
panic(err) | |
} | |
var rc conf.Registry | |
if err := c.Scan(&rc); err != nil { | |
panic(err) | |
} | |
app, cleanup, err := initApp(bc.Server, bc.Data, bc.Auth, bc.Service, &rc, logger) | |
if err != nil { | |
panic(err) | |
} | |
defer cleanup() | |
// start and wait for stop signal | |
if err := app.Run(); err != nil { | |
panic(err) | |
} | |
} |
结束语
这一篇主要是前期的准备工作,下一篇开始测试接口、测试服务注册与发现、加入链路追踪并测试。
感谢您的耐心阅读,动动手指点个赞吧。