# 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` | Метод | Описание | |-------|----------| | `Login` | Аутентификация пользователя (email + password) | | `Refresh` | Обновление access token по refresh token | | `Validate` | Валидация access token | | `Logout` | Выход (invalidate refresh token) | **Пример:** ```go // 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` | Комбинированная статистика баланса и заявок | **Пример:** ```go 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` | Получить информацию об инвайт-коде | **Пример:** ```go 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 **Пример:** ```go // Создание ТЗ 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 | **Пример:** ```go 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` | | `INSUFFICIENT_BALANCE` | `FailedPrecondition` | | Внутренние ошибки | `Internal` (без деталей) | ## Регистрация сервисов В `cmd/server/main.go`: ```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) ```bash # Получить список сервисов 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 тесты с моками ```go // Создать 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 файлов: ```yaml grpc: - name: smart-search-service enableReflection: true ``` ## Статистика - **Всего gRPC методов**: 16 - **Всего handlers**: 5 - **Строк кода handlers**: ~371 - **Proto файлов**: 5