Go workspace (go.work) with 5 microservices + shared library: - gateway (8080), auth-service (8081), purchaser-service (8082) - textile-service (8083), washing-service (8084) - shared: proto definitions, common packages Infrastructure: docker-compose for local dev, K8s manifests for K3s cluster deployment (mysql/redis/etcd + traefik ingress). Frontend (iloom-flatten) added as git submodule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
221 lines
5.3 KiB
Go
221 lines
5.3 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/muyuqingfeng/iloom/auth-service/internal/model"
|
|
"github.com/muyuqingfeng/iloom/auth-service/internal/repository"
|
|
"github.com/muyuqingfeng/iloom/shared/pkg/auth"
|
|
sgrpc "github.com/muyuqingfeng/iloom/shared/pkg/grpc"
|
|
)
|
|
|
|
var roleIDMap = map[string]string{
|
|
"purchaser": "r_010",
|
|
"textile": "r_011",
|
|
"washing": "r_012",
|
|
}
|
|
|
|
type AuthService struct {
|
|
profileRepo *repository.ProfileRepo
|
|
compRepo *repository.CompanyRepo
|
|
memberRepo *repository.MemberRepo
|
|
sysClient *sgrpc.SystemClient
|
|
jwt *auth.JWTManager
|
|
}
|
|
|
|
func NewAuthService(
|
|
profileRepo *repository.ProfileRepo,
|
|
compRepo *repository.CompanyRepo,
|
|
memberRepo *repository.MemberRepo,
|
|
sysClient *sgrpc.SystemClient,
|
|
jwt *auth.JWTManager,
|
|
) *AuthService {
|
|
return &AuthService{
|
|
profileRepo: profileRepo,
|
|
compRepo: compRepo,
|
|
memberRepo: memberRepo,
|
|
sysClient: sysClient,
|
|
jwt: jwt,
|
|
}
|
|
}
|
|
|
|
type RegisterRequest struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
CompanyName string `json:"company_name"`
|
|
Role string `json:"role"`
|
|
}
|
|
|
|
type LoginResponse struct {
|
|
AccessToken string `json:"access_token"`
|
|
RefreshToken string `json:"refresh_token"`
|
|
User *model.Profile `json:"user"`
|
|
Company *model.Company `json:"company"`
|
|
}
|
|
|
|
type ProfileWithCompany struct {
|
|
User *model.Profile `json:"user"`
|
|
Company *model.Company `json:"company,omitempty"`
|
|
}
|
|
|
|
func (s *AuthService) Register(ctx context.Context, req RegisterRequest) (*LoginResponse, error) {
|
|
roleID, ok := roleIDMap[req.Role]
|
|
if !ok {
|
|
return nil, errors.New("invalid role")
|
|
}
|
|
|
|
userID, err := s.sysClient.CreateUser(ctx, req.Username, req.Password, "", "", roleID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create user: %w", err)
|
|
}
|
|
|
|
now := time.Now()
|
|
companyID := uuid.New().String()
|
|
company := &model.Company{
|
|
ID: companyID,
|
|
Name: req.CompanyName,
|
|
Role: req.Role,
|
|
Status: 1,
|
|
CreatedAt: now,
|
|
}
|
|
if err := s.compRepo.Create(ctx, company); err != nil {
|
|
return nil, fmt.Errorf("create company: %w", err)
|
|
}
|
|
|
|
member := &model.CompanyMember{
|
|
ID: uuid.New().String(),
|
|
CompanyID: companyID,
|
|
UserID: userID,
|
|
Role: "owner",
|
|
IsMaster: true,
|
|
CreatedAt: now,
|
|
}
|
|
if err := s.memberRepo.Create(ctx, member); err != nil {
|
|
return nil, fmt.Errorf("create member: %w", err)
|
|
}
|
|
|
|
profile := &model.Profile{
|
|
ID: userID,
|
|
Username: req.Username,
|
|
CompanyID: companyID,
|
|
CompanyName: req.CompanyName,
|
|
Role: req.Role,
|
|
IsMaster: true,
|
|
CreatedAt: now,
|
|
}
|
|
|
|
return s.generateTokens(profile, company)
|
|
}
|
|
|
|
func (s *AuthService) Login(ctx context.Context, username, password string) (*LoginResponse, error) {
|
|
result, err := s.sysClient.Login(ctx, username, password, "")
|
|
if err != nil {
|
|
return nil, errors.New("invalid credentials")
|
|
}
|
|
|
|
profile, err := s.profileRepo.GetWithCompany(ctx, result.UserID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get profile: %w", err)
|
|
}
|
|
if profile == nil {
|
|
profile = &model.Profile{
|
|
ID: result.UserID,
|
|
Username: result.Username,
|
|
RealName: result.RealName,
|
|
Avatar: result.Avatar,
|
|
}
|
|
}
|
|
|
|
var company *model.Company
|
|
if profile.CompanyID != "" {
|
|
company, err = s.compRepo.GetByID(ctx, profile.CompanyID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get company: %w", err)
|
|
}
|
|
}
|
|
|
|
return s.generateTokens(profile, company)
|
|
}
|
|
|
|
func (s *AuthService) RefreshToken(ctx context.Context, refreshToken string) (string, error) {
|
|
userID, err := s.jwt.ParseRefreshToken(refreshToken)
|
|
if err != nil {
|
|
return "", fmt.Errorf("invalid refresh token: %w", err)
|
|
}
|
|
|
|
profile, err := s.profileRepo.GetWithCompany(ctx, userID)
|
|
if err != nil {
|
|
return "", fmt.Errorf("get user: %w", err)
|
|
}
|
|
if profile == nil {
|
|
return "", errors.New("user not found")
|
|
}
|
|
|
|
claims := auth.Claims{
|
|
UserID: profile.ID,
|
|
CompanyID: profile.CompanyID,
|
|
Role: profile.Role,
|
|
IsMaster: profile.IsMaster,
|
|
Username: profile.Username,
|
|
}
|
|
|
|
return s.jwt.GenerateAccessToken(claims)
|
|
}
|
|
|
|
func (s *AuthService) GetMe(ctx context.Context, userID string) (*ProfileWithCompany, error) {
|
|
profile, err := s.profileRepo.GetWithCompany(ctx, userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get profile: %w", err)
|
|
}
|
|
if profile == nil {
|
|
return nil, errors.New("user not found")
|
|
}
|
|
|
|
result := &ProfileWithCompany{User: profile}
|
|
if profile.CompanyID != "" {
|
|
company, err := s.compRepo.GetByID(ctx, profile.CompanyID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get company: %w", err)
|
|
}
|
|
result.Company = company
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (s *AuthService) generateTokens(profile *model.Profile, company *model.Company) (*LoginResponse, error) {
|
|
role := profile.Role
|
|
if role == "" && company != nil {
|
|
role = company.Role
|
|
}
|
|
|
|
claims := auth.Claims{
|
|
UserID: profile.ID,
|
|
CompanyID: profile.CompanyID,
|
|
Role: role,
|
|
IsMaster: profile.IsMaster,
|
|
Username: profile.Username,
|
|
}
|
|
|
|
accessToken, err := s.jwt.GenerateAccessToken(claims)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("generate access token: %w", err)
|
|
}
|
|
|
|
refreshToken, err := s.jwt.GenerateRefreshToken(profile.ID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("generate refresh token: %w", err)
|
|
}
|
|
|
|
return &LoginResponse{
|
|
AccessToken: accessToken,
|
|
RefreshToken: refreshToken,
|
|
User: profile,
|
|
Company: company,
|
|
}, nil
|
|
}
|