Files
backend/internal/service/request_service.go
2025-12-13 22:34:01 +05:00

203 lines
8.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}