initial commit
This commit is contained in:
246
internal/service/auth_service.go
Normal file
246
internal/service/auth_service.go
Normal file
@@ -0,0 +1,246 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.kirlllll.ru/volontery/backend/internal/database"
|
||||
"git.kirlllll.ru/volontery/backend/internal/pkg/jwt"
|
||||
"git.kirlllll.ru/volontery/backend/internal/pkg/password"
|
||||
"git.kirlllll.ru/volontery/backend/internal/repository"
|
||||
)
|
||||
|
||||
// AuthService предоставляет методы для аутентификации
|
||||
type AuthService struct {
|
||||
userRepo *repository.UserRepository
|
||||
authRepo *repository.AuthRepository
|
||||
rbacRepo *repository.RBACRepository
|
||||
jwtMgr *jwt.Manager
|
||||
}
|
||||
|
||||
// NewAuthService создает новый AuthService
|
||||
func NewAuthService(
|
||||
userRepo *repository.UserRepository,
|
||||
authRepo *repository.AuthRepository,
|
||||
rbacRepo *repository.RBACRepository,
|
||||
jwtMgr *jwt.Manager,
|
||||
) *AuthService {
|
||||
return &AuthService{
|
||||
userRepo: userRepo,
|
||||
authRepo: authRepo,
|
||||
rbacRepo: rbacRepo,
|
||||
jwtMgr: jwtMgr,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRequest - запрос на регистрацию
|
||||
type RegisterRequest struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Phone string `json:"phone,omitempty"`
|
||||
Latitude float64 `json:"latitude,omitempty"`
|
||||
Longitude float64 `json:"longitude,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
City string `json:"city,omitempty"`
|
||||
Bio string `json:"bio,omitempty"`
|
||||
}
|
||||
|
||||
// LoginRequest - запрос на вход
|
||||
type LoginRequest struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// AuthResponse - ответ с токенами
|
||||
type AuthResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ExpiresIn int64 `json:"expires_in"`
|
||||
User *UserInfo `json:"user"`
|
||||
}
|
||||
|
||||
// UserInfo - информация о пользователе
|
||||
type UserInfo struct {
|
||||
ID int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Verified bool `json:"email_verified"`
|
||||
}
|
||||
|
||||
// Register регистрирует нового пользователя
|
||||
func (s *AuthService) Register(ctx context.Context, req RegisterRequest) (*AuthResponse, error) {
|
||||
// Валидация
|
||||
if req.Email == "" {
|
||||
return nil, fmt.Errorf("email is required")
|
||||
}
|
||||
if !password.IsValid(req.Password) {
|
||||
return nil, fmt.Errorf("password must be at least 8 characters")
|
||||
}
|
||||
if req.FirstName == "" || req.LastName == "" {
|
||||
return nil, fmt.Errorf("first name and last name are required")
|
||||
}
|
||||
|
||||
// Проверка существования email
|
||||
exists, err := s.userRepo.EmailExists(ctx, req.Email)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check email: %w", err)
|
||||
}
|
||||
if exists {
|
||||
return nil, fmt.Errorf("email already registered")
|
||||
}
|
||||
|
||||
// Хеширование пароля
|
||||
hashedPassword, err := password.Hash(req.Password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
|
||||
// Создание пользователя
|
||||
user, err := s.userRepo.Create(ctx, database.CreateUserParams{
|
||||
Email: req.Email,
|
||||
PasswordHash: hashedPassword,
|
||||
FirstName: req.FirstName,
|
||||
LastName: req.LastName,
|
||||
Phone: stringToPgText(req.Phone),
|
||||
StMakepoint: req.Longitude,
|
||||
StMakepoint_2: req.Latitude,
|
||||
Address: stringToPgText(req.Address),
|
||||
City: stringToPgText(req.City),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create user: %w", err)
|
||||
}
|
||||
|
||||
// Назначение роли "requester" по умолчанию
|
||||
requesterRole, err := s.rbacRepo.GetRoleByName(ctx, "requester")
|
||||
if err == nil {
|
||||
_, _ = s.rbacRepo.AssignRoleToUser(ctx, database.AssignRoleToUserParams{
|
||||
UserID: user.ID,
|
||||
RoleID: requesterRole.ID,
|
||||
AssignedBy: int64ToPgInt8(user.ID), // сам себе назначил
|
||||
})
|
||||
}
|
||||
|
||||
// Генерация токенов
|
||||
return s.generateTokens(ctx, user.ID, user.Email, "", "")
|
||||
}
|
||||
|
||||
// Login выполняет вход пользователя
|
||||
func (s *AuthService) Login(ctx context.Context, req LoginRequest) (*AuthResponse, error) {
|
||||
// Получение пользователя
|
||||
user, err := s.userRepo.GetByEmail(ctx, req.Email)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid email or password")
|
||||
}
|
||||
|
||||
// Проверка пароля
|
||||
if err := password.Verify(user.PasswordHash, req.Password); err != nil {
|
||||
return nil, fmt.Errorf("invalid email or password")
|
||||
}
|
||||
|
||||
// Проверка блокировки
|
||||
if user.IsBlocked.Bool {
|
||||
return nil, fmt.Errorf("user account is blocked")
|
||||
}
|
||||
|
||||
// Обновление времени последнего входа
|
||||
_ = s.userRepo.UpdateLastLogin(ctx, user.ID)
|
||||
|
||||
// Генерация токенов
|
||||
return s.generateTokens(ctx, user.ID, user.Email, "", "")
|
||||
}
|
||||
|
||||
// RefreshTokens обновляет токены
|
||||
func (s *AuthService) RefreshTokens(ctx context.Context, refreshTokenString string) (*AuthResponse, error) {
|
||||
// Валидация refresh токена
|
||||
claims, err := s.jwtMgr.ValidateToken(refreshTokenString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid refresh token")
|
||||
}
|
||||
|
||||
// Проверка токена в БД
|
||||
storedToken, err := s.authRepo.GetRefreshToken(ctx, refreshTokenString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("refresh token not found or expired")
|
||||
}
|
||||
|
||||
// Отзыв старого токена
|
||||
_ = s.authRepo.RevokeRefreshToken(ctx, storedToken.ID)
|
||||
|
||||
// Получение пользователя
|
||||
user, err := s.userRepo.GetByID(ctx, claims.UserID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
|
||||
if user.IsBlocked.Bool {
|
||||
return nil, fmt.Errorf("user account is blocked")
|
||||
}
|
||||
|
||||
// Генерация новых токенов
|
||||
return s.generateTokens(ctx, user.ID, user.Email, "", "")
|
||||
}
|
||||
|
||||
// Logout выход пользователя
|
||||
func (s *AuthService) Logout(ctx context.Context, userID int64) error {
|
||||
return s.authRepo.RevokeAllUserTokens(ctx, userID)
|
||||
}
|
||||
|
||||
// generateTokens генерирует access и refresh токены
|
||||
func (s *AuthService) generateTokens(ctx context.Context, userID int64, email, userAgent, ipAddress string) (*AuthResponse, error) {
|
||||
// Генерация access токена
|
||||
accessToken, err := s.jwtMgr.GenerateAccessToken(userID, email)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate access token: %w", err)
|
||||
}
|
||||
|
||||
// Генерация refresh токена
|
||||
refreshToken, err := s.jwtMgr.GenerateRefreshToken(userID, email)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate refresh token: %w", err)
|
||||
}
|
||||
|
||||
// Сохранение refresh токена в БД
|
||||
expiresAt := time.Now().Add(s.jwtMgr.GetRefreshTokenDuration())
|
||||
_, err = s.authRepo.CreateRefreshToken(ctx, database.CreateRefreshTokenParams{
|
||||
UserID: userID,
|
||||
Token: refreshToken,
|
||||
ExpiresAt: timeToPgTimestamptz(expiresAt),
|
||||
UserAgent: stringToPgText(userAgent),
|
||||
IpAddress: nil, // IP адрес не передается в текущей реализации
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save refresh token: %w", err)
|
||||
}
|
||||
|
||||
// Получение информации о пользователе
|
||||
user, _ := s.userRepo.GetByID(ctx, userID)
|
||||
|
||||
return &AuthResponse{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresIn: int64(s.jwtMgr.GetAccessTokenDuration().Seconds()),
|
||||
User: &UserInfo{
|
||||
ID: userID,
|
||||
Email: email,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
Verified: user.EmailVerified.Bool,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// generateRandomToken генерирует случайный токен
|
||||
func generateRandomToken(length int) (string, error) {
|
||||
bytes := make([]byte, length)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(bytes), nil
|
||||
}
|
||||
27
internal/service/helpers.go
Normal file
27
internal/service/helpers.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
// Helper functions для конвертации типов Go в pgtype
|
||||
|
||||
func stringToPgText(s string) pgtype.Text {
|
||||
if s == "" {
|
||||
return pgtype.Text{Valid: false}
|
||||
}
|
||||
return pgtype.Text{String: s, Valid: true}
|
||||
}
|
||||
|
||||
func int64ToPgInt8(i int64) pgtype.Int8 {
|
||||
if i == 0 {
|
||||
return pgtype.Int8{Valid: false}
|
||||
}
|
||||
return pgtype.Int8{Int64: i, Valid: true}
|
||||
}
|
||||
|
||||
func timeToPgTimestamptz(t time.Time) pgtype.Timestamptz {
|
||||
return pgtype.Timestamptz{Time: t, Valid: true}
|
||||
}
|
||||
202
internal/service/request_service.go
Normal file
202
internal/service/request_service.go
Normal file
@@ -0,0 +1,202 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.kirlllll.ru/volontery/backend/internal/database"
|
||||
"git.kirlllll.ru/volontery/backend/internal/repository"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
// RequestService предоставляет методы для работы с заявками
|
||||
type RequestService struct {
|
||||
requestRepo *repository.RequestRepository
|
||||
}
|
||||
|
||||
// NewRequestService создает новый RequestService
|
||||
func NewRequestService(requestRepo *repository.RequestRepository) *RequestService {
|
||||
return &RequestService{
|
||||
requestRepo: requestRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateRequestInput - входные данные для создания заявки
|
||||
type CreateRequestInput struct {
|
||||
RequesterID int64 `json:"requester_id"`
|
||||
RequestTypeID int64 `json:"request_type_id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
Address string `json:"address"`
|
||||
City string `json:"city,omitempty"`
|
||||
DesiredCompletionDate *string `json:"desired_completion_date,omitempty"`
|
||||
Urgency string `json:"urgency"`
|
||||
ContactPhone string `json:"contact_phone,omitempty"`
|
||||
ContactNotes string `json:"contact_notes,omitempty"`
|
||||
}
|
||||
|
||||
// CreateRequest создает новую заявку
|
||||
func (s *RequestService) CreateRequest(ctx context.Context, input CreateRequestInput) (*database.CreateRequestRow, error) {
|
||||
// Валидация
|
||||
if input.Title == "" {
|
||||
return nil, fmt.Errorf("title is required")
|
||||
}
|
||||
if input.Description == "" {
|
||||
return nil, fmt.Errorf("description is required")
|
||||
}
|
||||
if input.Latitude == 0 || input.Longitude == 0 {
|
||||
return nil, fmt.Errorf("location is required")
|
||||
}
|
||||
|
||||
// Создание заявки
|
||||
return s.requestRepo.Create(ctx, database.CreateRequestParams{
|
||||
RequesterID: input.RequesterID,
|
||||
RequestTypeID: input.RequestTypeID,
|
||||
Title: input.Title,
|
||||
Description: input.Description,
|
||||
StMakepoint: input.Longitude,
|
||||
StMakepoint_2: input.Latitude,
|
||||
Address: input.Address,
|
||||
City: stringToPgText(input.City),
|
||||
Urgency: stringToPgText(input.Urgency),
|
||||
ContactPhone: stringToPgText(input.ContactPhone),
|
||||
ContactNotes: stringToPgText(input.ContactNotes),
|
||||
})
|
||||
}
|
||||
|
||||
// GetRequest получает заявку по ID
|
||||
func (s *RequestService) GetRequest(ctx context.Context, id int64) (*database.GetRequestByIDRow, error) {
|
||||
return s.requestRepo.GetByID(ctx, id)
|
||||
}
|
||||
|
||||
// GetUserRequests получает заявки пользователя
|
||||
func (s *RequestService) GetUserRequests(ctx context.Context, userID int64, limit, offset int32) ([]database.GetRequestsByRequesterRow, error) {
|
||||
return s.requestRepo.GetByRequester(ctx, database.GetRequestsByRequesterParams{
|
||||
RequesterID: userID,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
})
|
||||
}
|
||||
|
||||
// FindNearbyRequests ищет заявки рядом с точкой
|
||||
func (s *RequestService) FindNearbyRequests(ctx context.Context, lat, lon float64, radiusMeters float64, statuses []database.RequestStatus, limit, offset int32) ([]database.FindRequestsNearbyRow, error) {
|
||||
// Конвертируем []RequestStatus в []string
|
||||
statusStrings := make([]string, len(statuses))
|
||||
for i, status := range statuses {
|
||||
statusStrings[i] = string(status)
|
||||
}
|
||||
|
||||
return s.requestRepo.FindNearby(ctx, database.FindRequestsNearbyParams{
|
||||
StMakepoint: lon,
|
||||
StMakepoint_2: lat,
|
||||
Column3: statusStrings,
|
||||
StDwithin: radiusMeters,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
})
|
||||
}
|
||||
|
||||
// FindRequestsInBounds ищет заявки в прямоугольной области (для карты)
|
||||
func (s *RequestService) FindRequestsInBounds(ctx context.Context, statuses []database.RequestStatus, minLon, minLat, maxLon, maxLat float64) ([]database.FindRequestsInBoundsRow, error) {
|
||||
// Конвертируем []RequestStatus в []string
|
||||
statusStrings := make([]string, len(statuses))
|
||||
for i, status := range statuses {
|
||||
statusStrings[i] = string(status)
|
||||
}
|
||||
|
||||
return s.requestRepo.FindInBounds(ctx, database.FindRequestsInBoundsParams{
|
||||
Column1: statusStrings,
|
||||
StMakeenvelope: minLon,
|
||||
StMakeenvelope_2: minLat,
|
||||
StMakeenvelope_3: maxLon,
|
||||
StMakeenvelope_4: maxLat,
|
||||
})
|
||||
}
|
||||
|
||||
// CreateVolunteerResponse создает отклик волонтера на заявку
|
||||
func (s *RequestService) CreateVolunteerResponse(ctx context.Context, requestID, volunteerID int64, message string) (*database.VolunteerResponse, error) {
|
||||
return s.requestRepo.CreateVolunteerResponse(ctx, database.CreateVolunteerResponseParams{
|
||||
RequestID: requestID,
|
||||
VolunteerID: volunteerID,
|
||||
Message: stringToPgText(message),
|
||||
})
|
||||
}
|
||||
|
||||
// GetRequestResponses получает отклики на заявку
|
||||
func (s *RequestService) GetRequestResponses(ctx context.Context, requestID int64) ([]database.GetResponsesByRequestRow, error) {
|
||||
return s.requestRepo.GetResponsesByRequest(ctx, requestID)
|
||||
}
|
||||
|
||||
// ListRequestTypes получает список типов заявок
|
||||
func (s *RequestService) ListRequestTypes(ctx context.Context) ([]database.RequestType, error) {
|
||||
return s.requestRepo.ListTypes(ctx)
|
||||
}
|
||||
|
||||
// GetPendingModerationRequests получает заявки на модерации
|
||||
func (s *RequestService) GetPendingModerationRequests(ctx context.Context, limit, offset int32) ([]database.GetPendingModerationRequestsRow, error) {
|
||||
return s.requestRepo.GetPendingModerationRequests(ctx, limit, offset)
|
||||
}
|
||||
|
||||
// ApproveRequest одобряет заявку
|
||||
func (s *RequestService) ApproveRequest(ctx context.Context, requestID, moderatorID int64, comment *string) error {
|
||||
moderationComment := stringToPgText("")
|
||||
if comment != nil {
|
||||
moderationComment = stringToPgText(*comment)
|
||||
}
|
||||
|
||||
return s.requestRepo.ApproveRequest(ctx, database.ApproveRequestParams{
|
||||
ID: requestID,
|
||||
ModeratedBy: pgtype.Int8{
|
||||
Int64: moderatorID,
|
||||
Valid: true,
|
||||
},
|
||||
ModerationComment: moderationComment,
|
||||
})
|
||||
}
|
||||
|
||||
// RejectRequest отклоняет заявку
|
||||
func (s *RequestService) RejectRequest(ctx context.Context, requestID, moderatorID int64, comment string) error {
|
||||
if comment == "" {
|
||||
return fmt.Errorf("rejection comment is required")
|
||||
}
|
||||
|
||||
return s.requestRepo.RejectRequest(ctx, database.RejectRequestParams{
|
||||
ID: requestID,
|
||||
ModeratedBy: pgtype.Int8{
|
||||
Int64: moderatorID,
|
||||
Valid: true,
|
||||
},
|
||||
ModerationComment: stringToPgText(comment),
|
||||
})
|
||||
}
|
||||
|
||||
// GetModeratedRequests получает заявки, модерированные указанным модератором
|
||||
func (s *RequestService) GetModeratedRequests(ctx context.Context, moderatorID int64, limit, offset int32) ([]database.GetModeratedRequestsRow, error) {
|
||||
return s.requestRepo.GetModeratedRequests(ctx, moderatorID, limit, offset)
|
||||
}
|
||||
|
||||
// AcceptVolunteerResponse принимает отклик волонтера через хранимую процедуру
|
||||
func (s *RequestService) AcceptVolunteerResponse(ctx context.Context, responseID, requesterID int64) (*database.CallAcceptVolunteerResponseRow, error) {
|
||||
return s.requestRepo.AcceptVolunteerResponse(ctx, responseID, requesterID)
|
||||
}
|
||||
|
||||
// CompleteRequestWithRating завершает заявку с рейтингом через хранимую процедуру
|
||||
func (s *RequestService) CompleteRequestWithRating(ctx context.Context, requestID, requesterID int64, rating int32, comment *string) (*database.CallCompleteRequestWithRatingRow, error) {
|
||||
if rating < 1 || rating > 5 {
|
||||
return nil, fmt.Errorf("rating must be between 1 and 5")
|
||||
}
|
||||
return s.requestRepo.CompleteRequestWithRating(ctx, requestID, requesterID, rating, comment)
|
||||
}
|
||||
|
||||
// ModerateRequestProcedure модерирует заявку через хранимую процедуру
|
||||
func (s *RequestService) ModerateRequestProcedure(ctx context.Context, requestID, moderatorID int64, action string, comment *string) (*database.CallModerateRequestRow, error) {
|
||||
if action != "approve" && action != "reject" {
|
||||
return nil, fmt.Errorf("action must be 'approve' or 'reject'")
|
||||
}
|
||||
if action == "reject" && (comment == nil || *comment == "") {
|
||||
return nil, fmt.Errorf("comment is required when rejecting")
|
||||
}
|
||||
return s.requestRepo.ModerateRequestProcedure(ctx, requestID, moderatorID, action, comment)
|
||||
}
|
||||
96
internal/service/user_service.go
Normal file
96
internal/service/user_service.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.kirlllll.ru/volontery/backend/internal/database"
|
||||
"git.kirlllll.ru/volontery/backend/internal/repository"
|
||||
)
|
||||
|
||||
// UserService предоставляет методы для работы с пользователями
|
||||
type UserService struct {
|
||||
userRepo *repository.UserRepository
|
||||
rbacRepo *repository.RBACRepository
|
||||
}
|
||||
|
||||
// NewUserService создает новый UserService
|
||||
func NewUserService(userRepo *repository.UserRepository, rbacRepo *repository.RBACRepository) *UserService {
|
||||
return &UserService{
|
||||
userRepo: userRepo,
|
||||
rbacRepo: rbacRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserProfile получает профиль пользователя
|
||||
func (s *UserService) GetUserProfile(ctx context.Context, userID int64) (*database.GetUserProfileRow, error) {
|
||||
return s.userRepo.GetProfile(ctx, userID)
|
||||
}
|
||||
|
||||
// UpdateProfileInput - входные данные для обновления профиля
|
||||
type UpdateProfileInput struct {
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
Phone string `json:"phone,omitempty"`
|
||||
Bio string `json:"bio,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
City string `json:"city,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateUserProfile обновляет профиль пользователя
|
||||
func (s *UserService) UpdateUserProfile(ctx context.Context, userID int64, input UpdateProfileInput) error {
|
||||
return s.userRepo.UpdateProfile(ctx, database.UpdateUserProfileParams{
|
||||
UserID: userID,
|
||||
FirstName: stringToPgText(input.FirstName),
|
||||
LastName: stringToPgText(input.LastName),
|
||||
Phone: stringToPgText(input.Phone),
|
||||
Address: stringToPgText(input.Address),
|
||||
City: stringToPgText(input.City),
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateUserLocation обновляет местоположение пользователя
|
||||
func (s *UserService) UpdateUserLocation(ctx context.Context, userID int64, lat, lon float64) error {
|
||||
if lat == 0 || lon == 0 {
|
||||
return fmt.Errorf("invalid coordinates")
|
||||
}
|
||||
|
||||
return s.userRepo.UpdateLocation(ctx, database.UpdateUserLocationParams{
|
||||
ID: userID,
|
||||
StMakepoint: lon,
|
||||
StMakepoint_2: lat,
|
||||
})
|
||||
}
|
||||
|
||||
// VerifyEmail подтверждает email пользователя
|
||||
func (s *UserService) VerifyEmail(ctx context.Context, userID int64) error {
|
||||
return s.userRepo.VerifyEmail(ctx, userID)
|
||||
}
|
||||
|
||||
// GetUserRoles получает роли пользователя
|
||||
func (s *UserService) GetUserRoles(ctx context.Context, userID int64) ([]database.Role, error) {
|
||||
return s.rbacRepo.GetUserRoles(ctx, userID)
|
||||
}
|
||||
|
||||
// GetUserPermissions получает разрешения пользователя
|
||||
func (s *UserService) GetUserPermissions(ctx context.Context, userID int64) ([]database.GetUserPermissionsRow, error) {
|
||||
return s.rbacRepo.GetUserPermissions(ctx, userID)
|
||||
}
|
||||
|
||||
// HasPermission проверяет наличие разрешения у пользователя
|
||||
func (s *UserService) HasPermission(ctx context.Context, userID int64, permissionName string) (bool, error) {
|
||||
return s.rbacRepo.UserHasPermission(ctx, database.UserHasPermissionParams{
|
||||
ID: userID,
|
||||
Name: permissionName,
|
||||
})
|
||||
}
|
||||
|
||||
// AssignRole назначает роль пользователю
|
||||
func (s *UserService) AssignRole(ctx context.Context, userID, roleID, assignedBy int64) error {
|
||||
_, err := s.rbacRepo.AssignRoleToUser(ctx, database.AssignRoleToUserParams{
|
||||
UserID: userID,
|
||||
RoleID: roleID,
|
||||
AssignedBy: int64ToPgInt8(assignedBy),
|
||||
})
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user