package user import ( "context" "errors" "time" "gin_test/event" // repo 在本模块内,避免 user 模块依赖外部 model 层 "golang.org/x/crypto/bcrypt" ) type Service struct { repo *UserRepo bus *event.Dispatcher } func NewService(repo *UserRepo, bus *event.Dispatcher) *Service { return &Service{repo: repo, bus: bus} } func (s *Service) Register(ctx context.Context, username, password string) error { if username == "" || password == "" { return errors.New("username/password required") } hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return err } return s.repo.Create(ctx, username, string(hash)) } func (s *Service) Login(ctx context.Context, username, password string) (string, error) { u, err := s.repo.GetByUsername(ctx, username) if err != nil { return "", errors.New("invalid username or password") } if err := bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(password)); err != nil { return "", errors.New("invalid username or password") } // 通过事件让 JWT 模块签发 token(request/reply)。 reply := make(chan event.TokenIssueResult, 1) s.bus.Publish(event.TokenIssueRequested{ At: time.Now(), Username: u.Username, Reply: reply, }) select { case res := <-reply: if res.Err != "" { return "", errors.New(res.Err) } if res.Token == "" { return "", errors.New("empty token") } return res.Token, nil case <-time.After(2 * time.Second): return "", errors.New("token issue timeout") } }