259 lines
7.1 KiB
Go
259 lines
7.1 KiB
Go
|
|
package service
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
|
||
|
|
"eslogad-be/internal/contract"
|
||
|
|
"eslogad-be/internal/entities"
|
||
|
|
"eslogad-be/internal/transformer"
|
||
|
|
|
||
|
|
"github.com/google/uuid"
|
||
|
|
)
|
||
|
|
|
||
|
|
type VoteEventRepository interface {
|
||
|
|
Create(ctx context.Context, voteEvent *entities.VoteEvent) error
|
||
|
|
GetByID(ctx context.Context, id uuid.UUID) (*entities.VoteEvent, error)
|
||
|
|
GetActiveEvents(ctx context.Context) ([]*entities.VoteEvent, error)
|
||
|
|
List(ctx context.Context, limit, offset int) ([]*entities.VoteEvent, int64, error)
|
||
|
|
Update(ctx context.Context, voteEvent *entities.VoteEvent) error
|
||
|
|
Delete(ctx context.Context, id uuid.UUID) error
|
||
|
|
CreateCandidate(ctx context.Context, candidate *entities.Candidate) error
|
||
|
|
GetCandidatesByEventID(ctx context.Context, eventID uuid.UUID) ([]*entities.Candidate, error)
|
||
|
|
SubmitVote(ctx context.Context, vote *entities.Vote) error
|
||
|
|
HasUserVoted(ctx context.Context, userID, eventID uuid.UUID) (bool, error)
|
||
|
|
GetVoteResults(ctx context.Context, eventID uuid.UUID) (map[uuid.UUID]int64, error)
|
||
|
|
}
|
||
|
|
|
||
|
|
type VoteEventServiceImpl struct {
|
||
|
|
voteEventRepo VoteEventRepository
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewVoteEventService(voteEventRepo VoteEventRepository) *VoteEventServiceImpl {
|
||
|
|
return &VoteEventServiceImpl{
|
||
|
|
voteEventRepo: voteEventRepo,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *VoteEventServiceImpl) CreateVoteEvent(ctx context.Context, req *contract.CreateVoteEventRequest) (*contract.VoteEventResponse, error) {
|
||
|
|
if req.EndDate.Before(req.StartDate) {
|
||
|
|
return nil, errors.New("end date must be after start date")
|
||
|
|
}
|
||
|
|
|
||
|
|
voteEvent := &entities.VoteEvent{
|
||
|
|
Title: req.Title,
|
||
|
|
Description: req.Description,
|
||
|
|
StartDate: req.StartDate,
|
||
|
|
EndDate: req.EndDate,
|
||
|
|
IsActive: true,
|
||
|
|
ResultsOpen: false,
|
||
|
|
}
|
||
|
|
|
||
|
|
if req.ResultsOpen != nil {
|
||
|
|
voteEvent.ResultsOpen = *req.ResultsOpen
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := s.voteEventRepo.Create(ctx, voteEvent); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return transformer.VoteEventToContract(voteEvent), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *VoteEventServiceImpl) GetVoteEventByID(ctx context.Context, id uuid.UUID) (*contract.VoteEventResponse, error) {
|
||
|
|
voteEvent, err := s.voteEventRepo.GetByID(ctx, id)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return transformer.VoteEventToContract(voteEvent), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *VoteEventServiceImpl) GetActiveEvents(ctx context.Context) ([]contract.VoteEventResponse, error) {
|
||
|
|
events, err := s.voteEventRepo.GetActiveEvents(ctx)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
var responses []contract.VoteEventResponse
|
||
|
|
for _, event := range events {
|
||
|
|
responses = append(responses, *transformer.VoteEventToContract(event))
|
||
|
|
}
|
||
|
|
|
||
|
|
return responses, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *VoteEventServiceImpl) ListVoteEvents(ctx context.Context, req *contract.ListVoteEventsRequest) (*contract.ListVoteEventsResponse, error) {
|
||
|
|
page := req.Page
|
||
|
|
if page <= 0 {
|
||
|
|
page = 1
|
||
|
|
}
|
||
|
|
limit := req.Limit
|
||
|
|
if limit <= 0 {
|
||
|
|
limit = 10
|
||
|
|
}
|
||
|
|
|
||
|
|
offset := (page - 1) * limit
|
||
|
|
|
||
|
|
events, total, err := s.voteEventRepo.List(ctx, limit, offset)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
var responses []contract.VoteEventResponse
|
||
|
|
for _, event := range events {
|
||
|
|
responses = append(responses, *transformer.VoteEventToContract(event))
|
||
|
|
}
|
||
|
|
|
||
|
|
return &contract.ListVoteEventsResponse{
|
||
|
|
VoteEvents: responses,
|
||
|
|
Pagination: transformer.CreatePaginationResponse(int(total), page, limit),
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *VoteEventServiceImpl) UpdateVoteEvent(ctx context.Context, id uuid.UUID, req *contract.UpdateVoteEventRequest) (*contract.VoteEventResponse, error) {
|
||
|
|
if req.EndDate.Before(req.StartDate) {
|
||
|
|
return nil, errors.New("end date must be after start date")
|
||
|
|
}
|
||
|
|
|
||
|
|
voteEvent, err := s.voteEventRepo.GetByID(ctx, id)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
voteEvent.Title = req.Title
|
||
|
|
voteEvent.Description = req.Description
|
||
|
|
voteEvent.StartDate = req.StartDate
|
||
|
|
voteEvent.EndDate = req.EndDate
|
||
|
|
voteEvent.IsActive = req.IsActive
|
||
|
|
|
||
|
|
if req.ResultsOpen != nil {
|
||
|
|
voteEvent.ResultsOpen = *req.ResultsOpen
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := s.voteEventRepo.Update(ctx, voteEvent); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return transformer.VoteEventToContract(voteEvent), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *VoteEventServiceImpl) DeleteVoteEvent(ctx context.Context, id uuid.UUID) error {
|
||
|
|
return s.voteEventRepo.Delete(ctx, id)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *VoteEventServiceImpl) CreateCandidate(ctx context.Context, req *contract.CreateCandidateRequest) (*contract.CandidateResponse, error) {
|
||
|
|
voteEvent, err := s.voteEventRepo.GetByID(ctx, req.VoteEventID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
if !voteEvent.IsActive {
|
||
|
|
return nil, errors.New("cannot add candidates to inactive vote event")
|
||
|
|
}
|
||
|
|
|
||
|
|
candidate := &entities.Candidate{
|
||
|
|
VoteEventID: req.VoteEventID,
|
||
|
|
Name: req.Name,
|
||
|
|
ImageURL: req.ImageURL,
|
||
|
|
Description: req.Description,
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := s.voteEventRepo.CreateCandidate(ctx, candidate); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return transformer.CandidateToContract(candidate), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *VoteEventServiceImpl) GetCandidates(ctx context.Context, eventID uuid.UUID) ([]contract.CandidateResponse, error) {
|
||
|
|
candidates, err := s.voteEventRepo.GetCandidatesByEventID(ctx, eventID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
var responses []contract.CandidateResponse
|
||
|
|
for _, candidate := range candidates {
|
||
|
|
responses = append(responses, *transformer.CandidateToContract(candidate))
|
||
|
|
}
|
||
|
|
|
||
|
|
return responses, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *VoteEventServiceImpl) SubmitVote(ctx context.Context, userID uuid.UUID, req *contract.SubmitVoteRequest) (*contract.VoteResponse, error) {
|
||
|
|
voteEvent, err := s.voteEventRepo.GetByID(ctx, req.VoteEventID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
if !voteEvent.IsVotingOpen() {
|
||
|
|
return nil, errors.New("voting is not open for this event")
|
||
|
|
}
|
||
|
|
|
||
|
|
hasVoted, err := s.voteEventRepo.HasUserVoted(ctx, userID, req.VoteEventID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
if hasVoted {
|
||
|
|
return nil, errors.New("user has already voted for this event")
|
||
|
|
}
|
||
|
|
|
||
|
|
vote := &entities.Vote{
|
||
|
|
VoteEventID: req.VoteEventID,
|
||
|
|
CandidateID: req.CandidateID,
|
||
|
|
UserID: userID,
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := s.voteEventRepo.SubmitVote(ctx, vote); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return transformer.VoteToContract(vote), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *VoteEventServiceImpl) GetVoteResults(ctx context.Context, eventID uuid.UUID) (*contract.VoteResultsResponse, error) {
|
||
|
|
|
||
|
|
candidates, err := s.voteEventRepo.GetCandidatesByEventID(ctx, eventID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
voteResults, err := s.voteEventRepo.GetVoteResults(ctx, eventID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
var candidatesWithVotes []contract.CandidateWithVotesResponse
|
||
|
|
var totalVotes int64
|
||
|
|
|
||
|
|
for _, candidate := range candidates {
|
||
|
|
voteCount := voteResults[candidate.ID]
|
||
|
|
totalVotes += voteCount
|
||
|
|
|
||
|
|
candidatesWithVotes = append(candidatesWithVotes, contract.CandidateWithVotesResponse{
|
||
|
|
CandidateResponse: *transformer.CandidateToContract(candidate),
|
||
|
|
VoteCount: voteCount,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
return &contract.VoteResultsResponse{
|
||
|
|
VoteEventID: eventID,
|
||
|
|
Candidates: candidatesWithVotes,
|
||
|
|
TotalVotes: totalVotes,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *VoteEventServiceImpl) CheckVoteStatus(ctx context.Context, userID, eventID uuid.UUID) (*contract.CheckVoteStatusResponse, error) {
|
||
|
|
hasVoted, err := s.voteEventRepo.HasUserVoted(ctx, userID, eventID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
response := &contract.CheckVoteStatusResponse{
|
||
|
|
HasVoted: hasVoted,
|
||
|
|
}
|
||
|
|
|
||
|
|
return response, nil
|
||
|
|
}
|