initial commit
This commit is contained in:
14
user/internal/config/config.go
Normal file
14
user/internal/config/config.go
Normal file
@ -0,0 +1,14 @@
|
||||
package config
|
||||
|
||||
import "github.com/zeromicro/go-zero/zrpc"
|
||||
|
||||
type JwtAuth struct {
|
||||
AccessSecret string
|
||||
AccessExpire int64
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
zrpc.RpcServerConf
|
||||
DataSource string
|
||||
JwtAuth JwtAuth
|
||||
}
|
||||
50
user/internal/logic/getuserinfologic.go
Normal file
50
user/internal/logic/getuserinfologic.go
Normal file
@ -0,0 +1,50 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"godemo/user/internal/model"
|
||||
"godemo/user/internal/svc"
|
||||
"godemo/user/user"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type GetUserInfoLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserInfoLogic {
|
||||
return &GetUserInfoLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
func (l *GetUserInfoLogic) GetUserInfo(in *user.GetUserInfoRequest) (*user.GetUserInfoResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
var userModel model.User
|
||||
err := l.svcCtx.DB.Get(&userModel, "SELECT * FROM users WHERE user_id = $1", in.UserId)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, status.Error(status.Code(err), fmt.Sprintf("用户 %s 不存在", in.UserId))
|
||||
}
|
||||
return nil, status.Error(status.Code(err), err.Error())
|
||||
}
|
||||
|
||||
return &user.GetUserInfoResponse{
|
||||
UserId: userModel.UserId,
|
||||
Username: userModel.Username,
|
||||
Email: userModel.Email.String,
|
||||
CreatedAt: userModel.CreatedAt.Unix(),
|
||||
Roles: userModel.Roles,
|
||||
}, nil
|
||||
}
|
||||
69
user/internal/logic/loginlogic.go
Normal file
69
user/internal/logic/loginlogic.go
Normal file
@ -0,0 +1,69 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"godemo/user/internal/model"
|
||||
"godemo/user/internal/svc"
|
||||
"godemo/user/user"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type LoginLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
|
||||
return &LoginLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LoginLogic) Login(in *user.LoginRequest) (*user.LoginResponse, error) {
|
||||
// 1. 查找用户
|
||||
var u model.User
|
||||
err := l.svcCtx.DB.Get(&u, "SELECT * FROM users WHERE username = $1", in.Username)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, status.Error(codes.NotFound, "用户不存在")
|
||||
}
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
// 2. 校验密码(假设密码已加密存储)
|
||||
err = bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(in.Password))
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Unauthenticated, "密码错误")
|
||||
}
|
||||
|
||||
// 3. 签发 JWT Token
|
||||
claims := jwt.MapClaims{
|
||||
"sub": u.UserId,
|
||||
"exp": time.Now().Add(time.Minute * 15).Unix(), // 默认 15 分钟有效期
|
||||
"iat": time.Now().Unix(),
|
||||
"roles": u.Roles,
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
signedToken, err := token.SignedString([]byte(l.svcCtx.Config.JwtAuth.AccessSecret))
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Unavailable, "token生成失败")
|
||||
}
|
||||
|
||||
// 4. 返回响应
|
||||
return &user.LoginResponse{
|
||||
Token: signedToken,
|
||||
ExpiresAt: claims["exp"].(int64),
|
||||
}, nil
|
||||
}
|
||||
31
user/internal/logic/logoutlogic.go
Normal file
31
user/internal/logic/logoutlogic.go
Normal file
@ -0,0 +1,31 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"godemo/user/internal/svc"
|
||||
"godemo/user/user"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type LogoutLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewLogoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutLogic {
|
||||
return &LogoutLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// 用户登出,可选实现
|
||||
func (l *LogoutLogic) Logout(in *user.LogoutRequest) (*user.LogoutResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &user.LogoutResponse{}, nil
|
||||
}
|
||||
31
user/internal/logic/pinglogic.go
Normal file
31
user/internal/logic/pinglogic.go
Normal file
@ -0,0 +1,31 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"godemo/user/internal/svc"
|
||||
"godemo/user/user"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type PingLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewPingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PingLogic {
|
||||
return &PingLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// 健康检查
|
||||
func (l *PingLogic) Ping(in *user.PingRequest) (*user.PingResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &user.PingResponse{}, nil
|
||||
}
|
||||
90
user/internal/logic/registerlogic.go
Normal file
90
user/internal/logic/registerlogic.go
Normal file
@ -0,0 +1,90 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"godemo/user/internal/model"
|
||||
"godemo/user/internal/svc"
|
||||
"godemo/user/user"
|
||||
"time"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type RegisterLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterLogic {
|
||||
return &RegisterLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// 用户注册
|
||||
func (l *RegisterLogic) Register(in *user.RegisterRequest) (*user.RegisterResponse, error) {
|
||||
// 1. 检查用户名是否已存在
|
||||
var existingUser model.User
|
||||
err := l.svcCtx.DB.Get(&existingUser, "SELECT * FROM users WHERE username = $1", in.Username)
|
||||
if err == nil {
|
||||
// 用户名已存在
|
||||
return nil, status.Error(codes.AlreadyExists, "用户名已存在")
|
||||
}
|
||||
|
||||
// 2. 加密密码
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(in.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
// 密码加密失败
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
// 3. 转换 email 为 sql.NullString
|
||||
var email sql.NullString
|
||||
if in.Email != "" {
|
||||
email = sql.NullString{
|
||||
String: in.Email,
|
||||
Valid: true,
|
||||
}
|
||||
} else {
|
||||
email = sql.NullString{Valid: false}
|
||||
}
|
||||
|
||||
// 4. 保存新用户到数据库
|
||||
userModel := model.User{
|
||||
Username: in.Username,
|
||||
PasswordHash: string(hashedPassword),
|
||||
Email: email, // 使用 sql.NullString 类型存储 email
|
||||
Roles: []string{"user"}, // 默认角色为 "user"
|
||||
CreatedAt: time.Now(), // 当前时间戳
|
||||
}
|
||||
|
||||
// 执行插入数据库操作
|
||||
_, err = l.svcCtx.DB.Exec(
|
||||
"INSERT INTO users (username, password_hash, email, roles, created_at) VALUES ($1, $2, $3, $4, $5)",
|
||||
userModel.Username,
|
||||
userModel.PasswordHash,
|
||||
userModel.Email,
|
||||
pq.Array(userModel.Roles), // pq.Array 用于处理 PostgreSQL 数组类型
|
||||
userModel.CreatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
// 数据库插入失败
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
// 获取插入的记录的 UserId
|
||||
l.svcCtx.DB.Get(&existingUser, "SELECT * FROM users WHERE username = $1", in.Username)
|
||||
|
||||
// 5. 返回成功响应
|
||||
return &user.RegisterResponse{
|
||||
UserId: existingUser.UserId, // 将 UserId 转换为字符串
|
||||
}, nil
|
||||
}
|
||||
17
user/internal/model/user.go
Normal file
17
user/internal/model/user.go
Normal file
@ -0,0 +1,17 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
UserId string `db:"user_id"`
|
||||
Username string `db:"username"`
|
||||
Email sql.NullString `db:"email"`
|
||||
PasswordHash string `db:"password_hash"`
|
||||
Roles pq.StringArray `db:"roles"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
}
|
||||
54
user/internal/server/userserver.go
Normal file
54
user/internal/server/userserver.go
Normal file
@ -0,0 +1,54 @@
|
||||
// Code generated by goctl. DO NOT EDIT.
|
||||
// goctl 1.8.3
|
||||
// Source: user.proto
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"godemo/user/internal/logic"
|
||||
"godemo/user/internal/svc"
|
||||
"godemo/user/user"
|
||||
)
|
||||
|
||||
type UserServer struct {
|
||||
svcCtx *svc.ServiceContext
|
||||
user.UnimplementedUserServer
|
||||
}
|
||||
|
||||
func NewUserServer(svcCtx *svc.ServiceContext) *UserServer {
|
||||
return &UserServer{
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 健康检查
|
||||
func (s *UserServer) Ping(ctx context.Context, in *user.PingRequest) (*user.PingResponse, error) {
|
||||
l := logic.NewPingLogic(ctx, s.svcCtx)
|
||||
return l.Ping(in)
|
||||
}
|
||||
|
||||
// 用户注册
|
||||
func (s *UserServer) Register(ctx context.Context, in *user.RegisterRequest) (*user.RegisterResponse, error) {
|
||||
l := logic.NewRegisterLogic(ctx, s.svcCtx)
|
||||
return l.Register(in)
|
||||
}
|
||||
|
||||
// 用户登录,返回 JWT Token
|
||||
func (s *UserServer) Login(ctx context.Context, in *user.LoginRequest) (*user.LoginResponse, error) {
|
||||
l := logic.NewLoginLogic(ctx, s.svcCtx)
|
||||
return l.Login(in)
|
||||
}
|
||||
|
||||
// 用户登出,可选实现
|
||||
func (s *UserServer) Logout(ctx context.Context, in *user.LogoutRequest) (*user.LogoutResponse, error) {
|
||||
l := logic.NewLogoutLogic(ctx, s.svcCtx)
|
||||
return l.Logout(in)
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
func (s *UserServer) GetUserInfo(ctx context.Context, in *user.GetUserInfoRequest) (*user.GetUserInfoResponse, error) {
|
||||
l := logic.NewGetUserInfoLogic(ctx, s.svcCtx)
|
||||
return l.GetUserInfo(in)
|
||||
}
|
||||
23
user/internal/svc/servicecontext.go
Normal file
23
user/internal/svc/servicecontext.go
Normal file
@ -0,0 +1,23 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"godemo/user/internal/config"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
Config config.Config
|
||||
DB *sqlx.DB
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
db := sqlx.MustConnect("postgres", c.DataSource)
|
||||
db.SetMaxOpenConns(10)
|
||||
db.SetMaxIdleConns(5)
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
DB: db,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user