Chever John 9c39c8cbd7
init: iloom WMS monorepo with K8s deployment manifests
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>
2026-06-14 11:33:31 +08:00

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
}