2025-09-22 18:47:06 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"narasi-ahli-be/app/database/entity"
|
|
|
|
|
"narasi-ahli-be/app/module/chat/mapper"
|
|
|
|
|
"narasi-ahli-be/app/module/chat/repository"
|
|
|
|
|
"narasi-ahli-be/app/module/chat/request"
|
|
|
|
|
usersRepository "narasi-ahli-be/app/module/users/repository"
|
|
|
|
|
"narasi-ahli-be/utils/paginator"
|
|
|
|
|
utilSvc "narasi-ahli-be/utils/service"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/rs/zerolog"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type chatScheduleService struct {
|
|
|
|
|
chatScheduleRepository repository.ChatScheduleRepository
|
|
|
|
|
chatRepository repository.ChatRepository
|
2025-09-23 22:21:47 +00:00
|
|
|
chatService ChatService
|
2025-09-22 18:47:06 +00:00
|
|
|
chatScheduleMapper *mapper.ChatScheduleMapper
|
|
|
|
|
Log zerolog.Logger
|
|
|
|
|
UsersRepo usersRepository.UsersRepository
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ChatScheduleService interface {
|
|
|
|
|
// Chat Schedule CRUD operations
|
|
|
|
|
CreateChatSchedule(authToken string, req request.ChatScheduleCreateRequest) (dataResult *mapper.ChatScheduleResponse, err error)
|
|
|
|
|
GetAllChatSchedules(authToken string, req request.ChatScheduleQueryRequest) (schedules []*mapper.ChatScheduleResponse, paging paginator.Pagination, err error)
|
|
|
|
|
GetChatScheduleByID(authToken string, id uint) (schedule *mapper.ChatScheduleResponse, err error)
|
|
|
|
|
UpdateChatSchedule(authToken string, id uint, req request.ChatScheduleUpdateRequest) (err error)
|
|
|
|
|
DeleteChatSchedule(authToken string, id uint) (err error)
|
|
|
|
|
|
|
|
|
|
// Additional schedule operations
|
|
|
|
|
GetUpcomingSchedules(authToken string, limit int) (schedules []*mapper.ChatScheduleResponse, err error)
|
|
|
|
|
GetSchedulesByStatus(authToken string, status string) (schedules []*mapper.ChatScheduleResponse, err error)
|
|
|
|
|
SendScheduleReminder(authToken string, scheduleID uint) (err error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewChatScheduleService(
|
|
|
|
|
chatScheduleRepository repository.ChatScheduleRepository,
|
|
|
|
|
chatRepository repository.ChatRepository,
|
2025-09-23 22:21:47 +00:00
|
|
|
chatService ChatService,
|
2025-09-22 18:47:06 +00:00
|
|
|
log zerolog.Logger,
|
|
|
|
|
usersRepo usersRepository.UsersRepository,
|
|
|
|
|
) ChatScheduleService {
|
|
|
|
|
return &chatScheduleService{
|
|
|
|
|
chatScheduleRepository: chatScheduleRepository,
|
|
|
|
|
chatRepository: chatRepository,
|
2025-09-23 22:21:47 +00:00
|
|
|
chatService: chatService,
|
2025-09-22 18:47:06 +00:00
|
|
|
chatScheduleMapper: mapper.NewChatScheduleMapper(),
|
|
|
|
|
Log: log,
|
|
|
|
|
UsersRepo: usersRepo,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CreateChatSchedule - Create a new chat schedule
|
|
|
|
|
func (_i *chatScheduleService) CreateChatSchedule(authToken string, req request.ChatScheduleCreateRequest) (dataResult *mapper.ChatScheduleResponse, err error) {
|
|
|
|
|
userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
|
|
|
if userInfo == nil {
|
|
|
|
|
return nil, errors.New("user not found")
|
|
|
|
|
}
|
|
|
|
|
userID := userInfo.ID
|
|
|
|
|
|
|
|
|
|
// Validate that the scheduled time is in the future
|
|
|
|
|
if req.ScheduledAt.Before(time.Now()) {
|
|
|
|
|
return nil, errors.New("scheduled time must be in the future")
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-23 22:21:47 +00:00
|
|
|
var chatSessionID uint
|
|
|
|
|
|
|
|
|
|
// If ChatSessionID is not provided, create a new chat session
|
|
|
|
|
if req.ChatSessionID == nil {
|
|
|
|
|
// Create a new personal chat session for the schedule
|
|
|
|
|
chatSessionReq := request.ChatSessionCreateRequest{
|
|
|
|
|
Name: &req.Title, // Use schedule title as chat session name
|
|
|
|
|
Type: "group", // Default to group chat for schedules
|
|
|
|
|
UserIDs: []uint{}, // Empty for now, can be populated later
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chatSession, err := _i.chatService.CreateChatSession(authToken, chatSessionReq)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.New("failed to create chat session: " + err.Error())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chatSessionID = chatSession.ID
|
|
|
|
|
} else {
|
|
|
|
|
chatSessionID = *req.ChatSessionID
|
|
|
|
|
|
|
|
|
|
// Check if user is participant in the existing chat session
|
|
|
|
|
isParticipant, err := _i.chatRepository.CheckUserInChatSession(userID, chatSessionID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2025-09-22 18:47:06 +00:00
|
|
|
|
2025-09-23 22:21:47 +00:00
|
|
|
if !isParticipant {
|
|
|
|
|
return nil, errors.New("user is not a participant in this chat session")
|
|
|
|
|
}
|
2025-09-22 18:47:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert request to entity
|
|
|
|
|
schedule := _i.chatScheduleMapper.ToEntity(req)
|
2025-09-23 22:21:47 +00:00
|
|
|
schedule.ChatSessionID = chatSessionID
|
2025-09-22 18:47:06 +00:00
|
|
|
schedule.CreatedBy = userID
|
|
|
|
|
|
|
|
|
|
// Create schedule
|
|
|
|
|
result, err := _i.chatScheduleRepository.CreateChatSchedule(schedule)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert to response
|
|
|
|
|
dataResult = _i.chatScheduleMapper.ToResponse(result)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetAllChatSchedules - Get all chat schedules for a user
|
|
|
|
|
func (_i *chatScheduleService) GetAllChatSchedules(authToken string, req request.ChatScheduleQueryRequest) (schedules []*mapper.ChatScheduleResponse, paging paginator.Pagination, err error) {
|
|
|
|
|
userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
|
|
|
if userInfo == nil {
|
|
|
|
|
return nil, paginator.Pagination{}, errors.New("user not found")
|
|
|
|
|
}
|
|
|
|
|
userID := userInfo.ID
|
|
|
|
|
|
|
|
|
|
// Get schedules from repository
|
|
|
|
|
scheduleEntities, paging, err := _i.chatScheduleRepository.GetAllChatSchedules(userID, req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, paginator.Pagination{}, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert to response
|
|
|
|
|
schedules = _i.chatScheduleMapper.ToResponseList(scheduleEntities)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetChatScheduleByID - Get a specific chat schedule
|
|
|
|
|
func (_i *chatScheduleService) GetChatScheduleByID(authToken string, id uint) (schedule *mapper.ChatScheduleResponse, err error) {
|
|
|
|
|
// Get schedule from repository
|
|
|
|
|
scheduleEntity, err := _i.chatScheduleRepository.GetChatScheduleByID(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert to response
|
|
|
|
|
schedule = _i.chatScheduleMapper.ToResponse(scheduleEntity)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UpdateChatSchedule - Update a chat schedule
|
|
|
|
|
func (_i *chatScheduleService) UpdateChatSchedule(authToken string, id uint, req request.ChatScheduleUpdateRequest) (err error) {
|
|
|
|
|
userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
|
|
|
if userInfo == nil {
|
|
|
|
|
return errors.New("user not found")
|
|
|
|
|
}
|
|
|
|
|
userID := userInfo.ID
|
|
|
|
|
|
|
|
|
|
// Check if user is participant in the chat session
|
|
|
|
|
isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !isParticipant {
|
|
|
|
|
return errors.New("user is not a participant in this chat session")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get existing schedule to check if user is the creator
|
|
|
|
|
existingSchedule, err := _i.chatScheduleRepository.GetChatScheduleByID(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only creator can update the schedule
|
|
|
|
|
if existingSchedule.CreatedBy != userID {
|
|
|
|
|
return errors.New("only the creator can update this schedule")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate scheduled time if provided
|
|
|
|
|
if !req.ScheduledAt.IsZero() && req.ScheduledAt.Before(time.Now()) {
|
|
|
|
|
return errors.New("scheduled time must be in the future")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert request to entity
|
|
|
|
|
schedule := _i.chatScheduleMapper.ToUpdateEntity(req)
|
|
|
|
|
|
|
|
|
|
// Update schedule
|
|
|
|
|
err = _i.chatScheduleRepository.UpdateChatSchedule(id, schedule)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DeleteChatSchedule - Delete a chat schedule
|
|
|
|
|
func (_i *chatScheduleService) DeleteChatSchedule(authToken string, id uint) (err error) {
|
|
|
|
|
userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
|
|
|
if userInfo == nil {
|
|
|
|
|
return errors.New("user not found")
|
|
|
|
|
}
|
|
|
|
|
userID := userInfo.ID
|
|
|
|
|
|
|
|
|
|
// Get existing schedule to check if user is the creator
|
|
|
|
|
existingSchedule, err := _i.chatScheduleRepository.GetChatScheduleByID(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only creator can delete the schedule
|
|
|
|
|
if existingSchedule.CreatedBy != userID {
|
|
|
|
|
return errors.New("only the creator can delete this schedule")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Delete schedule
|
|
|
|
|
err = _i.chatScheduleRepository.DeleteChatSchedule(id)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetUpcomingSchedules - Get upcoming schedules for a user
|
|
|
|
|
func (_i *chatScheduleService) GetUpcomingSchedules(authToken string, limit int) (schedules []*mapper.ChatScheduleResponse, err error) {
|
|
|
|
|
userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
|
|
|
if userInfo == nil {
|
|
|
|
|
return nil, errors.New("user not found")
|
|
|
|
|
}
|
|
|
|
|
userID := userInfo.ID
|
|
|
|
|
|
|
|
|
|
// Get upcoming schedules from repository
|
|
|
|
|
scheduleEntities, err := _i.chatScheduleRepository.GetUpcomingSchedules(userID, limit)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert to response
|
|
|
|
|
schedules = _i.chatScheduleMapper.ToResponseList(scheduleEntities)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetSchedulesByStatus - Get schedules by status
|
|
|
|
|
func (_i *chatScheduleService) GetSchedulesByStatus(authToken string, status string) (schedules []*mapper.ChatScheduleResponse, err error) {
|
|
|
|
|
userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
|
|
|
if userInfo == nil {
|
|
|
|
|
return nil, errors.New("user not found")
|
|
|
|
|
}
|
|
|
|
|
userID := userInfo.ID
|
|
|
|
|
|
|
|
|
|
// Validate status
|
|
|
|
|
validStatuses := []string{"scheduled", "ongoing", "completed", "cancelled"}
|
|
|
|
|
isValidStatus := false
|
|
|
|
|
for _, validStatus := range validStatuses {
|
|
|
|
|
if status == validStatus {
|
|
|
|
|
isValidStatus = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !isValidStatus {
|
|
|
|
|
return nil, errors.New("invalid status")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get schedules by status from repository
|
|
|
|
|
scheduleEntities, err := _i.chatScheduleRepository.GetSchedulesByStatus(status)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Filter by user participation
|
|
|
|
|
var userSchedules []*entity.ChatSchedules
|
|
|
|
|
for _, schedule := range scheduleEntities {
|
|
|
|
|
isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, schedule.ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if isParticipant {
|
|
|
|
|
userSchedules = append(userSchedules, schedule)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert to response
|
|
|
|
|
schedules = _i.chatScheduleMapper.ToResponseList(userSchedules)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SendScheduleReminder - Send reminder for a schedule
|
|
|
|
|
func (_i *chatScheduleService) SendScheduleReminder(authToken string, scheduleID uint) (err error) {
|
|
|
|
|
userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
|
|
|
if userInfo == nil {
|
|
|
|
|
return errors.New("user not found")
|
|
|
|
|
}
|
|
|
|
|
userID := userInfo.ID
|
|
|
|
|
|
|
|
|
|
// Get schedule
|
|
|
|
|
schedule, err := _i.chatScheduleRepository.GetChatScheduleByID(scheduleID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only creator can send reminders
|
|
|
|
|
if schedule.CreatedBy != userID {
|
|
|
|
|
return errors.New("only the creator can send reminders")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if reminder was already sent
|
|
|
|
|
if schedule.IsReminderSent {
|
|
|
|
|
return errors.New("reminder has already been sent")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Implement actual reminder sending logic (email, push notification, etc.)
|
|
|
|
|
// For now, just update the reminder status
|
|
|
|
|
now := time.Now()
|
|
|
|
|
schedule.IsReminderSent = true
|
|
|
|
|
schedule.ReminderSentAt = &now
|
|
|
|
|
|
|
|
|
|
err = _i.chatScheduleRepository.UpdateChatSchedule(scheduleID, schedule)
|
|
|
|
|
return
|
|
|
|
|
}
|