7.6 KiB
gRPC Services
Архитектура
gRPC handlers разделены на отдельные структуры для каждого сервиса во избежание коллизий имён методов (например, GetInfo присутствует как в UserService, так и в InviteService).
Структура handlers
internal/grpc/
├── server.go # Инициализация handlers и регистрация
├── auth_handler.go # AuthService gRPC методы
├── user_handler.go # UserService gRPC методы
├── invite_handler.go # InviteService gRPC методы
├── request_handler.go # RequestService gRPC методы
├── supplier_handler.go # SupplierService gRPC методы
└── error_mapper.go # Маппинг ошибок в gRPC статусы
Сервисы
1. AuthService
Proto: api/proto/auth/auth.proto
| Метод | Описание |
|---|---|
Register |
Регистрация нового пользователя по инвайт-коду |
Login |
Аутентификация пользователя (email + password) |
Refresh |
Обновление access token по refresh token |
Validate |
Валидация access token |
Logout |
Выход (invalidate refresh token) |
Пример:
// Login
req := &auth.LoginRequest{
Email: "user@example.com",
Password: "password",
Ip: "127.0.0.1",
UserAgent: "MyApp/1.0",
}
resp, err := authClient.Login(ctx, req)
// resp.AccessToken, resp.RefreshToken
2. UserService
Proto: api/proto/user/user.proto
| Метод | Описание |
|---|---|
GetInfo |
Получить информацию о пользователе |
GetBalance |
Получить баланс |
GetStatistics |
Получить статистику заявок |
GetBalanceStatistics |
Комбинированная статистика баланса и заявок |
Пример:
req := &user.GetInfoRequest{UserId: 123}
resp, err := userClient.GetInfo(ctx, req)
// resp.Email, resp.Name, resp.CompanyName...
3. InviteService
Proto: api/proto/invite/invite.proto
| Метод | Описание |
|---|---|
Generate |
Сгенерировать инвайт-код |
GetInfo |
Получить информацию об инвайт-коде |
Пример:
req := &invite.GenerateRequest{
UserId: 123,
TtlDays: 30,
MaxUses: 5,
}
resp, err := inviteClient.Generate(ctx, req)
// resp.Code, resp.ExpiresAt
4. RequestService
Proto: api/proto/request/request.proto
| Метод | Описание |
|---|---|
CreateTZ |
Создать заявку и сгенерировать ТЗ (AI) |
ApproveTZ |
Подтвердить ТЗ и найти поставщиков (AI) |
GetMailingList |
Получить список заявок пользователя |
GetMailingListByID |
Получить детали конкретной заявки |
Особенности:
CreateTZ: поддерживает опциональныеfile_dataиfile_nameдля прикрепления файловApproveTZ: запускает поиск поставщиков через Perplexity API
Пример:
// Создание ТЗ
req := &request.CreateTZRequest{
UserId: 123,
RequestTxt: "Нужны поставщики автозапчастей",
FileData: fileBytes, // опционально
FileName: "specs.pdf", // опционально
}
resp, err := requestClient.CreateTZ(ctx, req)
// resp.RequestId, resp.TzText (сгенерировано AI)
// Подтверждение ТЗ
approveReq := &request.ApproveTZRequest{
RequestId: resp.RequestId,
FinalTz: "Отредактированное ТЗ",
UserId: 123,
}
approveResp, err := requestClient.ApproveTZ(ctx, approveReq)
// approveResp.MailingStatus (поставщики найдены)
5. SupplierService
Proto: api/proto/supplier/supplier.proto
| Метод | Описание |
|---|---|
ExportExcel |
Экспортировать список поставщиков в Excel |
Пример:
req := &supplier.ExportExcelRequest{
RequestId: "uuid-string",
UserId: 123,
}
resp, err := supplierClient.ExportExcel(ctx, req)
// resp.FileData, resp.FileName, resp.MimeType
Обработка ошибок
Все ошибки из service layer автоматически мапятся в gRPC статусы через errors.ToGRPCError():
| Код ошибки | gRPC Status |
|---|---|
AUTH_INVALID_CREDENTIALS |
Unauthenticated |
AUTH_INVALID_TOKEN |
Unauthenticated |
USER_NOT_FOUND |
NotFound |
REQUEST_NOT_FOUND |
NotFound |
INVITE_LIMIT_REACHED |
ResourceExhausted |
INVITE_INVALID_OR_EXPIRED |
FailedPrecondition |
EMAIL_ALREADY_EXISTS |
AlreadyExists |
INSUFFICIENT_BALANCE |
FailedPrecondition |
| Внутренние ошибки | Internal (без деталей) |
Регистрация сервисов
В cmd/server/main.go:
authHandler, userHandler, inviteHandler, requestHandler, supplierHandler :=
grpcServer.NewHandlers(pool, openAIKey, perplexityKey)
grpcEntry.AddRegFuncGrpc(func(s *grpc.Server) {
grpcServer.RegisterServices(s,
authHandler, userHandler, inviteHandler,
requestHandler, supplierHandler)
})
Context Flow
gRPC Request → Handler(ctx) → Service(ctx) → Repository(ctx) → pgx(ctx)
Все gRPC handlers принимают context.Context из gRPC request и прокидывают его через все слои.
Тестирование
Для тестирования gRPC методов можно использовать:
1. grpcurl (CLI)
# Получить список сервисов
grpcurl -plaintext localhost:9091 list
# Вызвать метод
grpcurl -plaintext \
-d '{"email":"user@example.com","password":"password","ip":"127.0.0.1","user_agent":"test"}' \
localhost:9091 auth.AuthService/Login
2. Unit тесты с моками
// Создать mock service
mockRequestService := &mockRequestService{
createTZFunc: func(ctx context.Context, userID int, txt string) (uuid.UUID, string, error) {
return uuid.New(), "Mock TZ", nil
},
}
// Создать handler с mock
handler := &grpc.RequestHandler{
requestService: mockRequestService,
}
// Тестировать
resp, err := handler.CreateTZ(ctx, &request.CreateTZRequest{...})
Метрики и трейсинг
Все gRPC методы автоматически отслеживаются через rk-boot:
- Метрики: запросы, ошибки, latency
- Трейсинг: distributed tracing через OpenTelemetry
- Логирование: все запросы логируются с request ID
Доступно на:
- Метрики:
http://localhost:9091/metrics - Health:
http://localhost:9091/health
Reflection
gRPC Reflection включен в config/boot.yaml, что позволяет использовать grpcurl и другие инструменты без .proto файлов:
grpc:
- name: smart-search-service
enableReflection: true
Статистика
- Всего gRPC методов: 17
- Всего handlers: 5
- Строк кода handlers: ~390
- Proto файлов: 5