feat: add chat schedule and the files
This commit is contained in:
parent
9659007a06
commit
94b0345f2b
|
|
@ -0,0 +1,23 @@
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChatScheduleFiles struct {
|
||||||
|
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
|
||||||
|
ChatScheduleID uint `json:"chat_schedule_id" gorm:"type:int4;not null;index"`
|
||||||
|
FileName string `json:"file_name" gorm:"type:varchar(255);not null"`
|
||||||
|
OriginalName string `json:"original_name" gorm:"type:varchar(255);not null"`
|
||||||
|
FilePath string `json:"file_path" gorm:"type:varchar(500);not null"`
|
||||||
|
FileSize int64 `json:"file_size" gorm:"type:int8"`
|
||||||
|
MimeType string `json:"mime_type" gorm:"type:varchar(100)"`
|
||||||
|
FileType string `json:"file_type" gorm:"type:varchar(20);not null;check:file_type IN ('article', 'journal', 'video', 'audio', 'document', 'other')"`
|
||||||
|
Description string `json:"description" gorm:"type:text"`
|
||||||
|
IsRequired bool `json:"is_required" gorm:"default:false"`
|
||||||
|
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
ChatSchedule *ChatSchedules `json:"chat_schedule" gorm:"foreignKey:ChatScheduleID;references:ID"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"narasi-ahli-be/app/database/entity/users"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChatSchedules struct {
|
||||||
|
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
|
||||||
|
ChatSessionID uint `json:"chat_session_id" gorm:"type:int4;not null;index"`
|
||||||
|
Title string `json:"title" gorm:"type:varchar(255);not null"`
|
||||||
|
Description string `json:"description" gorm:"type:text"`
|
||||||
|
Summary string `json:"summary" gorm:"type:text"`
|
||||||
|
ScheduledAt time.Time `json:"scheduled_at" gorm:"not null"`
|
||||||
|
Duration int `json:"duration" gorm:"type:int4;default:60"` // duration in minutes
|
||||||
|
Status string `json:"status" gorm:"type:varchar(20);not null;default:'scheduled';check:status IN ('scheduled', 'ongoing', 'completed', 'cancelled')"`
|
||||||
|
IsReminderSent bool `json:"is_reminder_sent" gorm:"default:false"`
|
||||||
|
ReminderSentAt *time.Time `json:"reminder_sent_at"`
|
||||||
|
CreatedBy uint `json:"created_by" gorm:"type:int4;not null;index"`
|
||||||
|
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
ChatSession *ChatSessions `json:"chat_session" gorm:"foreignKey:ChatSessionID;references:ID"`
|
||||||
|
Creator *users.Users `json:"creator" gorm:"foreignKey:CreatedBy;references:ID"`
|
||||||
|
Files []*ChatScheduleFiles `json:"files" gorm:"foreignKey:ChatScheduleID;references:ID"`
|
||||||
|
}
|
||||||
|
|
@ -124,6 +124,8 @@ func Models() []interface{} {
|
||||||
entity.ChatMessages{},
|
entity.ChatMessages{},
|
||||||
entity.ChatParticipants{},
|
entity.ChatParticipants{},
|
||||||
entity.ChatSessions{},
|
entity.ChatSessions{},
|
||||||
|
entity.ChatSchedules{},
|
||||||
|
entity.ChatScheduleFiles{},
|
||||||
entity.AIChatSessions{},
|
entity.AIChatSessions{},
|
||||||
entity.AIChatMessages{},
|
entity.AIChatMessages{},
|
||||||
entity.AIChatLogs{},
|
entity.AIChatLogs{},
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,18 @@ type ChatRouter struct {
|
||||||
var NewChatModule = fx.Options(
|
var NewChatModule = fx.Options(
|
||||||
// register repository of Chat module
|
// register repository of Chat module
|
||||||
fx.Provide(repository.NewChatRepository),
|
fx.Provide(repository.NewChatRepository),
|
||||||
|
fx.Provide(repository.NewChatScheduleRepository),
|
||||||
|
fx.Provide(repository.NewChatScheduleFileRepository),
|
||||||
|
|
||||||
// register service of Chat module
|
// register service of Chat module
|
||||||
fx.Provide(service.NewChatService),
|
fx.Provide(service.NewChatService),
|
||||||
|
fx.Provide(service.NewChatScheduleService),
|
||||||
|
fx.Provide(service.NewChatScheduleFileService),
|
||||||
|
|
||||||
// register controller of Chat module
|
// register controller of Chat module
|
||||||
fx.Provide(controller.NewController),
|
fx.Provide(controller.NewController),
|
||||||
|
fx.Provide(controller.NewChatScheduleController),
|
||||||
|
fx.Provide(controller.NewChatScheduleFileController),
|
||||||
|
|
||||||
// register router of Chat module
|
// register router of Chat module
|
||||||
fx.Provide(NewChatRouter),
|
fx.Provide(NewChatRouter),
|
||||||
|
|
@ -42,6 +48,8 @@ func NewChatRouter(fiber *fiber.App, controller *controller.Controller) *ChatRou
|
||||||
func (_i *ChatRouter) RegisterChatRoutes() {
|
func (_i *ChatRouter) RegisterChatRoutes() {
|
||||||
// define controllers
|
// define controllers
|
||||||
chatController := _i.Controller.Chat
|
chatController := _i.Controller.Chat
|
||||||
|
chatScheduleController := _i.Controller.ChatSchedule
|
||||||
|
chatScheduleFileController := _i.Controller.ChatScheduleFile
|
||||||
|
|
||||||
// define routes
|
// define routes
|
||||||
_i.App.Route("/chat", func(router fiber.Router) {
|
_i.App.Route("/chat", func(router fiber.Router) {
|
||||||
|
|
@ -66,5 +74,27 @@ func (_i *ChatRouter) RegisterChatRoutes() {
|
||||||
messageRouter.Put("/:id", chatController.UpdateChatMessage)
|
messageRouter.Put("/:id", chatController.UpdateChatMessage)
|
||||||
messageRouter.Delete("/:id", chatController.DeleteChatMessage)
|
messageRouter.Delete("/:id", chatController.DeleteChatMessage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Chat Schedule routes
|
||||||
|
router.Route("/schedules", func(scheduleRouter fiber.Router) {
|
||||||
|
scheduleRouter.Get("/", chatScheduleController.GetAllChatSchedules)
|
||||||
|
scheduleRouter.Get("/upcoming", chatScheduleController.GetUpcomingSchedules)
|
||||||
|
scheduleRouter.Get("/status/:status", chatScheduleController.GetSchedulesByStatus)
|
||||||
|
scheduleRouter.Get("/:id", chatScheduleController.GetChatScheduleByID)
|
||||||
|
scheduleRouter.Post("/", chatScheduleController.CreateChatSchedule)
|
||||||
|
scheduleRouter.Put("/:id", chatScheduleController.UpdateChatSchedule)
|
||||||
|
scheduleRouter.Delete("/:id", chatScheduleController.DeleteChatSchedule)
|
||||||
|
scheduleRouter.Post("/:id/reminder", chatScheduleController.SendScheduleReminder)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Chat Schedule File routes
|
||||||
|
router.Route("/schedule-files", func(fileRouter fiber.Router) {
|
||||||
|
fileRouter.Get("/", chatScheduleFileController.GetChatScheduleFiles)
|
||||||
|
fileRouter.Get("/:id", chatScheduleFileController.GetChatScheduleFileByID)
|
||||||
|
fileRouter.Post("/:chatScheduleId", chatScheduleFileController.UploadChatScheduleFile)
|
||||||
|
fileRouter.Put("/:id", chatScheduleFileController.UpdateChatScheduleFile)
|
||||||
|
fileRouter.Delete("/:id", chatScheduleFileController.DeleteChatScheduleFile)
|
||||||
|
fileRouter.Get("/viewer/:filename", chatScheduleFileController.Viewer)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,320 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"narasi-ahli-be/app/module/chat/request"
|
||||||
|
"narasi-ahli-be/app/module/chat/service"
|
||||||
|
"narasi-ahli-be/utils/paginator"
|
||||||
|
utilRes "narasi-ahli-be/utils/response"
|
||||||
|
utilVal "narasi-ahli-be/utils/validator"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type chatScheduleController struct {
|
||||||
|
chatScheduleService service.ChatScheduleService
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatScheduleController interface {
|
||||||
|
// Chat Schedule endpoints
|
||||||
|
GetAllChatSchedules(c *fiber.Ctx) error
|
||||||
|
GetChatScheduleByID(c *fiber.Ctx) error
|
||||||
|
CreateChatSchedule(c *fiber.Ctx) error
|
||||||
|
UpdateChatSchedule(c *fiber.Ctx) error
|
||||||
|
DeleteChatSchedule(c *fiber.Ctx) error
|
||||||
|
|
||||||
|
// Additional schedule endpoints
|
||||||
|
GetUpcomingSchedules(c *fiber.Ctx) error
|
||||||
|
GetSchedulesByStatus(c *fiber.Ctx) error
|
||||||
|
SendScheduleReminder(c *fiber.Ctx) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChatScheduleController(chatScheduleService service.ChatScheduleService) ChatScheduleController {
|
||||||
|
return &chatScheduleController{
|
||||||
|
chatScheduleService: chatScheduleService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllChatSchedules - Get all chat schedules for a user
|
||||||
|
// @Summary Get all chat schedules
|
||||||
|
// @Description API for getting all chat schedules for authenticated user
|
||||||
|
// @Tags Chat Schedule
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param chatSessionId query string false "Chat Session ID"
|
||||||
|
// @Param status query string false "Schedule status (scheduled, ongoing, completed, cancelled)"
|
||||||
|
// @Param createdBy query string false "Created by user ID"
|
||||||
|
// @Param dateFrom query string false "Date from (YYYY-MM-DD)"
|
||||||
|
// @Param dateTo query string false "Date to (YYYY-MM-DD)"
|
||||||
|
// @Param req query paginator.Pagination false "pagination parameters"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedules [get]
|
||||||
|
func (_i *chatScheduleController) GetAllChatSchedules(c *fiber.Ctx) error {
|
||||||
|
paginate, err := paginator.Paginate(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
reqContext := request.ChatScheduleQueryRequestContext{
|
||||||
|
ChatSessionID: c.Query("chatSessionId"),
|
||||||
|
Status: c.Query("status"),
|
||||||
|
CreatedBy: c.Query("createdBy"),
|
||||||
|
DateFrom: c.Query("dateFrom"),
|
||||||
|
DateTo: c.Query("dateTo"),
|
||||||
|
}
|
||||||
|
req := reqContext.ToParamRequest()
|
||||||
|
req.Pagination = *paginate
|
||||||
|
|
||||||
|
chatSchedules, paging, err := _i.chatScheduleService.GetAllChatSchedules(authToken, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Chat schedules successfully retrieved"},
|
||||||
|
Data: chatSchedules,
|
||||||
|
Meta: paging,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChatScheduleByID - Get one chat schedule
|
||||||
|
// @Summary Get one chat schedule
|
||||||
|
// @Description API for getting one chat schedule
|
||||||
|
// @Tags Chat Schedule
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param id path int true "Chat Schedule ID"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedules/{id} [get]
|
||||||
|
func (_i *chatScheduleController) GetChatScheduleByID(c *fiber.Ctx) error {
|
||||||
|
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
chatSchedule, err := _i.chatScheduleService.GetChatScheduleByID(authToken, uint(id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Chat schedule successfully retrieved"},
|
||||||
|
Data: chatSchedule,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateChatSchedule - Create chat schedule
|
||||||
|
// @Summary Create chat schedule
|
||||||
|
// @Description API for creating a new chat schedule
|
||||||
|
// @Tags Chat Schedule
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param payload body request.ChatScheduleCreateRequest true "Required payload"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedules [post]
|
||||||
|
func (_i *chatScheduleController) CreateChatSchedule(c *fiber.Ctx) error {
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
req := new(request.ChatScheduleCreateRequest)
|
||||||
|
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataResult, err := _i.chatScheduleService.CreateChatSchedule(authToken, *req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Chat schedule successfully created"},
|
||||||
|
Data: dataResult,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateChatSchedule - Update chat schedule
|
||||||
|
// @Summary Update chat schedule
|
||||||
|
// @Description API for updating chat schedule (only creator can update)
|
||||||
|
// @Tags Chat Schedule
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param id path int true "Chat Schedule ID"
|
||||||
|
// @Param payload body request.ChatScheduleUpdateRequest true "Required payload"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedules/{id} [put]
|
||||||
|
func (_i *chatScheduleController) UpdateChatSchedule(c *fiber.Ctx) error {
|
||||||
|
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
req := new(request.ChatScheduleUpdateRequest)
|
||||||
|
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _i.chatScheduleService.UpdateChatSchedule(authToken, uint(id), *req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Chat schedule successfully updated"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteChatSchedule - Delete chat schedule
|
||||||
|
// @Summary Delete chat schedule
|
||||||
|
// @Description API for deleting chat schedule (only creator can delete)
|
||||||
|
// @Tags Chat Schedule
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param id path int true "Chat Schedule ID"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedules/{id} [delete]
|
||||||
|
func (_i *chatScheduleController) DeleteChatSchedule(c *fiber.Ctx) error {
|
||||||
|
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
err = _i.chatScheduleService.DeleteChatSchedule(authToken, uint(id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Chat schedule successfully deleted"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUpcomingSchedules - Get upcoming schedules for a user
|
||||||
|
// @Summary Get upcoming schedules
|
||||||
|
// @Description API for getting upcoming chat schedules for authenticated user
|
||||||
|
// @Tags Chat Schedule
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param limit query int false "Limit number of results" default(10)
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedules/upcoming [get]
|
||||||
|
func (_i *chatScheduleController) GetUpcomingSchedules(c *fiber.Ctx) error {
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
limit := 10 // default limit
|
||||||
|
if limitStr := c.Query("limit"); limitStr != "" {
|
||||||
|
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 {
|
||||||
|
limit = parsedLimit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chatSchedules, err := _i.chatScheduleService.GetUpcomingSchedules(authToken, limit)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Upcoming chat schedules successfully retrieved"},
|
||||||
|
Data: chatSchedules,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSchedulesByStatus - Get schedules by status
|
||||||
|
// @Summary Get schedules by status
|
||||||
|
// @Description API for getting chat schedules by status
|
||||||
|
// @Tags Chat Schedule
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param status path string true "Schedule status (scheduled, ongoing, completed, cancelled)"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedules/status/{status} [get]
|
||||||
|
func (_i *chatScheduleController) GetSchedulesByStatus(c *fiber.Ctx) error {
|
||||||
|
status := c.Params("status")
|
||||||
|
if status == "" {
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: false,
|
||||||
|
Messages: utilRes.Messages{"Status parameter is required"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
chatSchedules, err := _i.chatScheduleService.GetSchedulesByStatus(authToken, status)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Chat schedules by status successfully retrieved"},
|
||||||
|
Data: chatSchedules,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendScheduleReminder - Send reminder for a schedule
|
||||||
|
// @Summary Send schedule reminder
|
||||||
|
// @Description API for sending reminder for a chat schedule (only creator can send)
|
||||||
|
// @Tags Chat Schedule
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param id path int true "Chat Schedule ID"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedules/{id}/reminder [post]
|
||||||
|
func (_i *chatScheduleController) SendScheduleReminder(c *fiber.Ctx) error {
|
||||||
|
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
err = _i.chatScheduleService.SendScheduleReminder(authToken, uint(id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Schedule reminder successfully sent"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,233 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"narasi-ahli-be/app/module/chat/request"
|
||||||
|
"narasi-ahli-be/app/module/chat/service"
|
||||||
|
utilRes "narasi-ahli-be/utils/response"
|
||||||
|
utilVal "narasi-ahli-be/utils/validator"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type chatScheduleFileController struct {
|
||||||
|
chatScheduleFileService service.ChatScheduleFileService
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatScheduleFileController interface {
|
||||||
|
// File upload endpoints
|
||||||
|
UploadChatScheduleFile(c *fiber.Ctx) error
|
||||||
|
GetChatScheduleFiles(c *fiber.Ctx) error
|
||||||
|
GetChatScheduleFileByID(c *fiber.Ctx) error
|
||||||
|
UpdateChatScheduleFile(c *fiber.Ctx) error
|
||||||
|
DeleteChatScheduleFile(c *fiber.Ctx) error
|
||||||
|
Viewer(c *fiber.Ctx) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChatScheduleFileController(chatScheduleFileService service.ChatScheduleFileService) ChatScheduleFileController {
|
||||||
|
return &chatScheduleFileController{
|
||||||
|
chatScheduleFileService: chatScheduleFileService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadChatScheduleFile - Upload file for chat schedule
|
||||||
|
// @Summary Upload chat schedule file
|
||||||
|
// @Description API for uploading file for chat schedule
|
||||||
|
// @Tags Chat Schedule File
|
||||||
|
// @Security Bearer
|
||||||
|
// @Produce json
|
||||||
|
// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param files formData file true "Upload file" multiple true
|
||||||
|
// @Param chatScheduleId path int true "Chat Schedule ID"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedule-files/{chatScheduleId} [post]
|
||||||
|
func (_i *chatScheduleFileController) UploadChatScheduleFile(c *fiber.Ctx) error {
|
||||||
|
// Get chat schedule ID from path
|
||||||
|
id, err := strconv.ParseUint(c.Params("chatScheduleId"), 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _i.chatScheduleFileService.UploadChatScheduleFile(c, uint(id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Chat schedule files successfully uploaded"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChatScheduleFiles - Get files for a chat schedule
|
||||||
|
// @Summary Get chat schedule files
|
||||||
|
// @Description API for getting files for a specific chat schedule
|
||||||
|
// @Tags Chat Schedule File
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param chatScheduleId query uint true "Chat Schedule ID"
|
||||||
|
// @Param fileType query string false "File type filter"
|
||||||
|
// @Param isRequired query bool false "Required file filter"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedule-files [get]
|
||||||
|
func (_i *chatScheduleFileController) GetChatScheduleFiles(c *fiber.Ctx) error {
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
req := request.ChatScheduleFileQueryRequest{}
|
||||||
|
|
||||||
|
// Parse chat schedule ID
|
||||||
|
if chatScheduleIdStr := c.Query("chatScheduleId"); chatScheduleIdStr != "" {
|
||||||
|
if chatScheduleId, err := strconv.ParseUint(chatScheduleIdStr, 10, 0); err == nil {
|
||||||
|
chatScheduleIdUint := uint(chatScheduleId)
|
||||||
|
req.ChatScheduleID = &chatScheduleIdUint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse file type
|
||||||
|
if fileType := c.Query("fileType"); fileType != "" {
|
||||||
|
req.FileType = &fileType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse is required
|
||||||
|
if isRequiredStr := c.Query("isRequired"); isRequiredStr != "" {
|
||||||
|
if isRequired, err := strconv.ParseBool(isRequiredStr); err == nil {
|
||||||
|
req.IsRequired = &isRequired
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := _i.chatScheduleFileService.GetChatScheduleFiles(authToken, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Chat schedule files successfully retrieved"},
|
||||||
|
Data: files,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChatScheduleFileByID - Get one chat schedule file
|
||||||
|
// @Summary Get one chat schedule file
|
||||||
|
// @Description API for getting one chat schedule file
|
||||||
|
// @Tags Chat Schedule File
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param id path int true "Chat Schedule File ID"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedule-files/{id} [get]
|
||||||
|
func (_i *chatScheduleFileController) GetChatScheduleFileByID(c *fiber.Ctx) error {
|
||||||
|
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
file, err := _i.chatScheduleFileService.GetChatScheduleFileByID(authToken, uint(id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Chat schedule file successfully retrieved"},
|
||||||
|
Data: file,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateChatScheduleFile - Update chat schedule file
|
||||||
|
// @Summary Update chat schedule file
|
||||||
|
// @Description API for updating chat schedule file
|
||||||
|
// @Tags Chat Schedule File
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param id path int true "Chat Schedule File ID"
|
||||||
|
// @Param payload body request.ChatScheduleFileUpdateRequest true "Required payload"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedule-files/{id} [put]
|
||||||
|
func (_i *chatScheduleFileController) UpdateChatScheduleFile(c *fiber.Ctx) error {
|
||||||
|
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
req := new(request.ChatScheduleFileUpdateRequest)
|
||||||
|
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _i.chatScheduleFileService.UpdateChatScheduleFile(authToken, uint(id), *req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Chat schedule file successfully updated"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteChatScheduleFile - Delete chat schedule file
|
||||||
|
// @Summary Delete chat schedule file
|
||||||
|
// @Description API for deleting chat schedule file
|
||||||
|
// @Tags Chat Schedule File
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param id path int true "Chat Schedule File ID"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedule-files/{id} [delete]
|
||||||
|
func (_i *chatScheduleFileController) DeleteChatScheduleFile(c *fiber.Ctx) error {
|
||||||
|
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
err = _i.chatScheduleFileService.DeleteChatScheduleFile(authToken, uint(id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Chat schedule file successfully deleted"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Viewer - View chat schedule file
|
||||||
|
// @Summary View chat schedule file
|
||||||
|
// @Description API for viewing chat schedule file
|
||||||
|
// @Tags Chat Schedule File
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
||||||
|
// @Param filename path string true "Chat Schedule File Name"
|
||||||
|
// @Success 200 {file} file
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /chat/schedule-files/viewer/{filename} [get]
|
||||||
|
func (_i *chatScheduleFileController) Viewer(c *fiber.Ctx) error {
|
||||||
|
return _i.chatScheduleFileService.Viewer(c)
|
||||||
|
}
|
||||||
|
|
@ -4,10 +4,14 @@ import "narasi-ahli-be/app/module/chat/service"
|
||||||
|
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
Chat ChatController
|
Chat ChatController
|
||||||
|
ChatSchedule ChatScheduleController
|
||||||
|
ChatScheduleFile ChatScheduleFileController
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewController(ChatService service.ChatService) *Controller {
|
func NewController(ChatService service.ChatService, ChatScheduleService service.ChatScheduleService, ChatScheduleFileService service.ChatScheduleFileService) *Controller {
|
||||||
return &Controller{
|
return &Controller{
|
||||||
Chat: NewChatController(ChatService),
|
Chat: NewChatController(ChatService),
|
||||||
|
ChatSchedule: NewChatScheduleController(ChatScheduleService),
|
||||||
|
ChatScheduleFile: NewChatScheduleFileController(ChatScheduleFileService),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
package mapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"narasi-ahli-be/app/database/entity"
|
||||||
|
"narasi-ahli-be/app/module/chat/request"
|
||||||
|
"narasi-ahli-be/app/module/chat/response"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChatScheduleResponse - Response structure for chat schedule
|
||||||
|
type ChatScheduleResponse struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
ChatSessionID uint `json:"chat_session_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Summary string `json:"summary"`
|
||||||
|
ScheduledAt string `json:"scheduled_at"`
|
||||||
|
Duration int `json:"duration"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
IsReminderSent bool `json:"is_reminder_sent"`
|
||||||
|
ReminderSentAt *string `json:"reminder_sent_at"`
|
||||||
|
CreatedBy uint `json:"created_by"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
ChatSession *response.ChatSessionResponse `json:"chat_session,omitempty"`
|
||||||
|
Creator *response.UserBasicInfo `json:"creator,omitempty"`
|
||||||
|
Files []*response.ChatScheduleFileResponse `json:"files,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChatScheduleMapper - Mapper for chat schedule
|
||||||
|
type ChatScheduleMapper struct{}
|
||||||
|
|
||||||
|
// NewChatScheduleMapper - Create new chat schedule mapper
|
||||||
|
func NewChatScheduleMapper() *ChatScheduleMapper {
|
||||||
|
return &ChatScheduleMapper{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToResponse - Convert entity to response
|
||||||
|
func (m *ChatScheduleMapper) ToResponse(schedule *entity.ChatSchedules) *ChatScheduleResponse {
|
||||||
|
if schedule == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleResponse := &ChatScheduleResponse{
|
||||||
|
ID: schedule.ID,
|
||||||
|
ChatSessionID: schedule.ChatSessionID,
|
||||||
|
Title: schedule.Title,
|
||||||
|
Description: schedule.Description,
|
||||||
|
Summary: schedule.Summary,
|
||||||
|
ScheduledAt: schedule.ScheduledAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||||
|
Duration: schedule.Duration,
|
||||||
|
Status: schedule.Status,
|
||||||
|
IsReminderSent: schedule.IsReminderSent,
|
||||||
|
CreatedBy: schedule.CreatedBy,
|
||||||
|
CreatedAt: schedule.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||||
|
UpdatedAt: schedule.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if schedule.ReminderSentAt != nil {
|
||||||
|
reminderSentAt := schedule.ReminderSentAt.Format("2006-01-02T15:04:05Z07:00")
|
||||||
|
scheduleResponse.ReminderSentAt = &reminderSentAt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map chat session
|
||||||
|
if schedule.ChatSession != nil {
|
||||||
|
scheduleResponse.ChatSession = ChatSessionResponseMapper(schedule.ChatSession)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map creator
|
||||||
|
if schedule.Creator != nil {
|
||||||
|
scheduleResponse.Creator = &response.UserBasicInfo{
|
||||||
|
ID: schedule.Creator.ID,
|
||||||
|
Username: schedule.Creator.Username,
|
||||||
|
Fullname: schedule.Creator.Fullname,
|
||||||
|
Email: schedule.Creator.Email,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map files
|
||||||
|
if len(schedule.Files) > 0 {
|
||||||
|
scheduleResponse.Files = make([]*response.ChatScheduleFileResponse, len(schedule.Files))
|
||||||
|
for i, file := range schedule.Files {
|
||||||
|
scheduleResponse.Files[i] = m.ToFileResponse(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scheduleResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFileResponse - Convert file entity to response
|
||||||
|
func (m *ChatScheduleMapper) ToFileResponse(file *entity.ChatScheduleFiles) *response.ChatScheduleFileResponse {
|
||||||
|
if file == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response.ChatScheduleFileResponse{
|
||||||
|
ID: file.ID,
|
||||||
|
ChatScheduleID: file.ChatScheduleID,
|
||||||
|
FileName: file.FileName,
|
||||||
|
OriginalName: file.OriginalName,
|
||||||
|
FilePath: file.FilePath,
|
||||||
|
FileSize: file.FileSize,
|
||||||
|
MimeType: file.MimeType,
|
||||||
|
FileType: file.FileType,
|
||||||
|
Description: file.Description,
|
||||||
|
IsRequired: file.IsRequired,
|
||||||
|
CreatedAt: file.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||||
|
UpdatedAt: file.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToEntity - Convert request to entity
|
||||||
|
func (m *ChatScheduleMapper) ToEntity(req request.ChatScheduleCreateRequest) *entity.ChatSchedules {
|
||||||
|
schedule := &entity.ChatSchedules{
|
||||||
|
ChatSessionID: req.ChatSessionID,
|
||||||
|
Title: req.Title,
|
||||||
|
Description: req.Description,
|
||||||
|
Summary: req.Summary,
|
||||||
|
ScheduledAt: req.ScheduledAt,
|
||||||
|
Duration: req.Duration,
|
||||||
|
Status: "scheduled",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Files will be attached separately using file IDs
|
||||||
|
|
||||||
|
return schedule
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUpdateEntity - Convert update request to entity
|
||||||
|
func (m *ChatScheduleMapper) ToUpdateEntity(req request.ChatScheduleUpdateRequest) *entity.ChatSchedules {
|
||||||
|
schedule := &entity.ChatSchedules{}
|
||||||
|
|
||||||
|
if req.Title != "" {
|
||||||
|
schedule.Title = req.Title
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Description != "" {
|
||||||
|
schedule.Description = req.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Summary != "" {
|
||||||
|
schedule.Summary = req.Summary
|
||||||
|
}
|
||||||
|
|
||||||
|
if !req.ScheduledAt.IsZero() {
|
||||||
|
schedule.ScheduledAt = req.ScheduledAt
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Duration > 0 {
|
||||||
|
schedule.Duration = req.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Status != "" {
|
||||||
|
schedule.Status = req.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// Files will be attached separately using file IDs
|
||||||
|
|
||||||
|
return schedule
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToResponseList - Convert entity list to response list
|
||||||
|
func (m *ChatScheduleMapper) ToResponseList(schedules []*entity.ChatSchedules) []*ChatScheduleResponse {
|
||||||
|
if schedules == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
responses := make([]*ChatScheduleResponse, len(schedules))
|
||||||
|
for i, schedule := range schedules {
|
||||||
|
responses[i] = m.ToResponse(schedule)
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
package mapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"narasi-ahli-be/app/database/entity"
|
||||||
|
"narasi-ahli-be/app/module/chat/request"
|
||||||
|
"narasi-ahli-be/app/module/chat/response"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChatScheduleFileMapper - Mapper for chat schedule file
|
||||||
|
type ChatScheduleFileMapper struct{}
|
||||||
|
|
||||||
|
// NewChatScheduleFileMapper - Create new chat schedule file mapper
|
||||||
|
func NewChatScheduleFileMapper() *ChatScheduleFileMapper {
|
||||||
|
return &ChatScheduleFileMapper{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToResponse - Convert entity to response
|
||||||
|
func (m *ChatScheduleFileMapper) ToResponse(file *entity.ChatScheduleFiles) *response.ChatScheduleFileResponse {
|
||||||
|
if file == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response.ChatScheduleFileResponse{
|
||||||
|
ID: file.ID,
|
||||||
|
ChatScheduleID: file.ChatScheduleID,
|
||||||
|
FileName: file.FileName,
|
||||||
|
OriginalName: file.OriginalName,
|
||||||
|
FilePath: file.FilePath,
|
||||||
|
FileSize: file.FileSize,
|
||||||
|
MimeType: file.MimeType,
|
||||||
|
FileType: file.FileType,
|
||||||
|
Description: file.Description,
|
||||||
|
IsRequired: file.IsRequired,
|
||||||
|
CreatedAt: file.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||||
|
UpdatedAt: file.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToEntity - Convert upload request to entity
|
||||||
|
func (m *ChatScheduleFileMapper) ToEntity(req request.ChatScheduleFileUploadRequest) *entity.ChatScheduleFiles {
|
||||||
|
return &entity.ChatScheduleFiles{
|
||||||
|
ChatScheduleID: req.ChatScheduleID,
|
||||||
|
FileType: req.FileType,
|
||||||
|
Description: req.Description,
|
||||||
|
IsRequired: req.IsRequired,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUpdateEntity - Convert update request to entity
|
||||||
|
func (m *ChatScheduleFileMapper) ToUpdateEntity(req request.ChatScheduleFileUpdateRequest) *entity.ChatScheduleFiles {
|
||||||
|
file := &entity.ChatScheduleFiles{}
|
||||||
|
|
||||||
|
if req.FileName != "" {
|
||||||
|
file.FileName = req.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.OriginalName != "" {
|
||||||
|
file.OriginalName = req.OriginalName
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.FilePath != "" {
|
||||||
|
file.FilePath = req.FilePath
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.FileSize > 0 {
|
||||||
|
file.FileSize = req.FileSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.MimeType != "" {
|
||||||
|
file.MimeType = req.MimeType
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.FileType != "" {
|
||||||
|
file.FileType = req.FileType
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Description != "" {
|
||||||
|
file.Description = req.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.IsRequired != nil {
|
||||||
|
file.IsRequired = *req.IsRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToResponseList - Convert entity list to response list
|
||||||
|
func (m *ChatScheduleFileMapper) ToResponseList(files []*entity.ChatScheduleFiles) []*response.ChatScheduleFileResponse {
|
||||||
|
if files == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
responses := make([]*response.ChatScheduleFileResponse, len(files))
|
||||||
|
for i, file := range files {
|
||||||
|
responses[i] = m.ToResponse(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"narasi-ahli-be/app/database"
|
||||||
|
"narasi-ahli-be/app/database/entity"
|
||||||
|
"narasi-ahli-be/app/module/chat/request"
|
||||||
|
"narasi-ahli-be/utils/paginator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type chatScheduleRepository struct {
|
||||||
|
DB *database.Database
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatScheduleRepository interface {
|
||||||
|
// Chat Schedule CRUD operations
|
||||||
|
CreateChatSchedule(schedule *entity.ChatSchedules) (result *entity.ChatSchedules, err error)
|
||||||
|
GetAllChatSchedules(userId uint, req request.ChatScheduleQueryRequest) (schedules []*entity.ChatSchedules, paging paginator.Pagination, err error)
|
||||||
|
GetChatScheduleByID(id uint) (schedule *entity.ChatSchedules, err error)
|
||||||
|
UpdateChatSchedule(id uint, schedule *entity.ChatSchedules) (err error)
|
||||||
|
DeleteChatSchedule(id uint) (err error)
|
||||||
|
|
||||||
|
// Chat Schedule File operations
|
||||||
|
CreateChatScheduleFile(file *entity.ChatScheduleFiles) (result *entity.ChatScheduleFiles, err error)
|
||||||
|
GetChatScheduleFilesByScheduleID(scheduleID uint) (files []*entity.ChatScheduleFiles, err error)
|
||||||
|
UpdateChatScheduleFile(id uint, file *entity.ChatScheduleFiles) (err error)
|
||||||
|
DeleteChatScheduleFile(id uint) (err error)
|
||||||
|
|
||||||
|
// Utility methods
|
||||||
|
CheckUserInChatSchedule(userId uint, scheduleID uint) (isParticipant bool, err error)
|
||||||
|
GetUpcomingSchedules(userId uint, limit int) (schedules []*entity.ChatSchedules, err error)
|
||||||
|
GetSchedulesByStatus(status string) (schedules []*entity.ChatSchedules, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChatScheduleRepository(db *database.Database) ChatScheduleRepository {
|
||||||
|
return &chatScheduleRepository{
|
||||||
|
DB: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chat Schedule Repository Methods
|
||||||
|
func (_i *chatScheduleRepository) CreateChatSchedule(schedule *entity.ChatSchedules) (result *entity.ChatSchedules, err error) {
|
||||||
|
err = _i.DB.DB.Create(schedule).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _i.DB.DB.Preload("Creator").
|
||||||
|
Preload("ChatSession").
|
||||||
|
Preload("Files").
|
||||||
|
First(&result, schedule.ID).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_i *chatScheduleRepository) GetAllChatSchedules(userId uint, req request.ChatScheduleQueryRequest) (schedules []*entity.ChatSchedules, paging paginator.Pagination, err error) {
|
||||||
|
// Get chat schedules where user is a participant in the chat session
|
||||||
|
query := _i.DB.DB.Model(&entity.ChatSchedules{}).
|
||||||
|
Joins("INNER JOIN chat_sessions cs ON chat_schedules.chat_session_id = cs.id").
|
||||||
|
Joins("INNER JOIN chat_participants cp ON cs.id = cp.chat_session_id").
|
||||||
|
Where("cp.user_id = ? AND cp.is_active = true", userId)
|
||||||
|
|
||||||
|
// Apply filters
|
||||||
|
if req.ChatSessionID != nil {
|
||||||
|
query = query.Where("chat_schedules.chat_session_id = ?", *req.ChatSessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Status != nil {
|
||||||
|
query = query.Where("chat_schedules.status = ?", *req.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.CreatedBy != nil {
|
||||||
|
query = query.Where("chat_schedules.created_by = ?", *req.CreatedBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.DateFrom != nil {
|
||||||
|
query = query.Where("DATE(chat_schedules.scheduled_at) >= ?", req.DateFrom.Format("2006-01-02"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.DateTo != nil {
|
||||||
|
query = query.Where("DATE(chat_schedules.scheduled_at) <= ?", req.DateTo.Format("2006-01-02"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include relationships
|
||||||
|
query = query.Preload("Creator").
|
||||||
|
Preload("ChatSession").
|
||||||
|
Preload("Files")
|
||||||
|
|
||||||
|
// Order by scheduled_at asc (upcoming first)
|
||||||
|
query = query.Order("chat_schedules.scheduled_at ASC")
|
||||||
|
|
||||||
|
// Apply pagination
|
||||||
|
var count int64
|
||||||
|
query.Count(&count)
|
||||||
|
req.Pagination.Count = count
|
||||||
|
pagingResult := paginator.Paging(&req.Pagination)
|
||||||
|
|
||||||
|
err = query.Offset(pagingResult.Offset).Limit(pagingResult.Limit).Find(&schedules).Error
|
||||||
|
paging = *pagingResult
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_i *chatScheduleRepository) GetChatScheduleByID(id uint) (schedule *entity.ChatSchedules, err error) {
|
||||||
|
err = _i.DB.DB.Preload("Creator").
|
||||||
|
Preload("ChatSession").
|
||||||
|
Preload("Files").
|
||||||
|
First(&schedule, id).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_i *chatScheduleRepository) UpdateChatSchedule(id uint, schedule *entity.ChatSchedules) (err error) {
|
||||||
|
err = _i.DB.DB.Model(&entity.ChatSchedules{}).Where("id = ?", id).Updates(schedule).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_i *chatScheduleRepository) DeleteChatSchedule(id uint) (err error) {
|
||||||
|
err = _i.DB.DB.Delete(&entity.ChatSchedules{}, id).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chat Schedule File Repository Methods
|
||||||
|
func (_i *chatScheduleRepository) CreateChatScheduleFile(file *entity.ChatScheduleFiles) (result *entity.ChatScheduleFiles, err error) {
|
||||||
|
err = _i.DB.DB.Create(file).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _i.DB.DB.First(&result, file.ID).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_i *chatScheduleRepository) GetChatScheduleFilesByScheduleID(scheduleID uint) (files []*entity.ChatScheduleFiles, err error) {
|
||||||
|
err = _i.DB.DB.Model(&entity.ChatScheduleFiles{}).Where("chat_schedule_id = ?", scheduleID).Find(&files).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_i *chatScheduleRepository) UpdateChatScheduleFile(id uint, file *entity.ChatScheduleFiles) (err error) {
|
||||||
|
err = _i.DB.DB.Model(&entity.ChatScheduleFiles{}).Where("id = ?", id).Updates(file).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_i *chatScheduleRepository) DeleteChatScheduleFile(id uint) (err error) {
|
||||||
|
err = _i.DB.DB.Delete(&entity.ChatScheduleFiles{}, id).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility Methods
|
||||||
|
func (_i *chatScheduleRepository) CheckUserInChatSchedule(userId uint, scheduleID uint) (isParticipant bool, err error) {
|
||||||
|
err = _i.DB.DB.Model(&entity.ChatParticipants{}).
|
||||||
|
Select("COUNT(*) > 0").
|
||||||
|
Joins("INNER JOIN chat_sessions cs ON chat_participants.chat_session_id = cs.id").
|
||||||
|
Where("cs.id = ? AND chat_participants.user_id = ? AND chat_participants.is_active = true", scheduleID, userId).
|
||||||
|
Scan(&isParticipant).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_i *chatScheduleRepository) GetUpcomingSchedules(userId uint, limit int) (schedules []*entity.ChatSchedules, err error) {
|
||||||
|
err = _i.DB.DB.Model(&entity.ChatSchedules{}).
|
||||||
|
Joins("INNER JOIN chat_sessions cs ON chat_schedules.chat_session_id = cs.id").
|
||||||
|
Joins("INNER JOIN chat_participants cp ON cs.id = cp.chat_session_id").
|
||||||
|
Where("cp.user_id = ? AND cp.is_active = true AND chat_schedules.scheduled_at > NOW() AND chat_schedules.status = 'scheduled'", userId).
|
||||||
|
Preload("Creator").
|
||||||
|
Preload("ChatSession").
|
||||||
|
Preload("Files").
|
||||||
|
Order("chat_schedules.scheduled_at ASC").
|
||||||
|
Limit(limit).
|
||||||
|
Find(&schedules).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_i *chatScheduleRepository) GetSchedulesByStatus(status string) (schedules []*entity.ChatSchedules, err error) {
|
||||||
|
err = _i.DB.DB.Model(&entity.ChatSchedules{}).
|
||||||
|
Where("status = ?", status).
|
||||||
|
Preload("Creator").
|
||||||
|
Preload("ChatSession").
|
||||||
|
Preload("Files").
|
||||||
|
Order("scheduled_at ASC").
|
||||||
|
Find(&schedules).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"narasi-ahli-be/app/database"
|
||||||
|
"narasi-ahli-be/app/database/entity"
|
||||||
|
"narasi-ahli-be/app/module/chat/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
type chatScheduleFileRepository struct {
|
||||||
|
DB *database.Database
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatScheduleFileRepository interface {
|
||||||
|
// File CRUD operations
|
||||||
|
CreateChatScheduleFile(file *entity.ChatScheduleFiles) (result *entity.ChatScheduleFiles, err error)
|
||||||
|
GetChatScheduleFiles(req request.ChatScheduleFileQueryRequest) (files []*entity.ChatScheduleFiles, err error)
|
||||||
|
GetChatScheduleFileByID(id uint) (file *entity.ChatScheduleFiles, err error)
|
||||||
|
GetChatScheduleFileByFilename(filename string) (file *entity.ChatScheduleFiles, err error)
|
||||||
|
UpdateChatScheduleFile(id uint, file *entity.ChatScheduleFiles) (err error)
|
||||||
|
DeleteChatScheduleFile(id uint) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChatScheduleFileRepository(db *database.Database) ChatScheduleFileRepository {
|
||||||
|
return &chatScheduleFileRepository{
|
||||||
|
DB: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateChatScheduleFile - Create a new chat schedule file
|
||||||
|
func (_i *chatScheduleFileRepository) CreateChatScheduleFile(file *entity.ChatScheduleFiles) (result *entity.ChatScheduleFiles, err error) {
|
||||||
|
err = _i.DB.DB.Create(file).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _i.DB.DB.First(&result, file.ID).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChatScheduleFiles - Get files for chat schedule with filters
|
||||||
|
func (_i *chatScheduleFileRepository) GetChatScheduleFiles(req request.ChatScheduleFileQueryRequest) (files []*entity.ChatScheduleFiles, err error) {
|
||||||
|
query := _i.DB.DB.Model(&entity.ChatScheduleFiles{})
|
||||||
|
|
||||||
|
// Apply filters
|
||||||
|
if req.ChatScheduleID != nil {
|
||||||
|
query = query.Where("chat_schedule_id = ?", *req.ChatScheduleID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.FileType != nil {
|
||||||
|
query = query.Where("file_type = ?", *req.FileType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.IsRequired != nil {
|
||||||
|
query = query.Where("is_required = ?", *req.IsRequired)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order by created_at desc (newest first)
|
||||||
|
query = query.Order("created_at DESC")
|
||||||
|
|
||||||
|
err = query.Find(&files).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChatScheduleFileByID - Get a specific chat schedule file
|
||||||
|
func (_i *chatScheduleFileRepository) GetChatScheduleFileByID(id uint) (file *entity.ChatScheduleFiles, err error) {
|
||||||
|
err = _i.DB.DB.First(&file, id).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateChatScheduleFile - Update a chat schedule file
|
||||||
|
func (_i *chatScheduleFileRepository) UpdateChatScheduleFile(id uint, file *entity.ChatScheduleFiles) (err error) {
|
||||||
|
err = _i.DB.DB.Model(&entity.ChatScheduleFiles{}).Where("id = ?", id).Updates(file).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChatScheduleFileByFilename - Get a chat schedule file by filename
|
||||||
|
func (_i *chatScheduleFileRepository) GetChatScheduleFileByFilename(filename string) (file *entity.ChatScheduleFiles, err error) {
|
||||||
|
err = _i.DB.DB.Where("file_name = ?", filename).First(&file).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteChatScheduleFile - Delete a chat schedule file
|
||||||
|
func (_i *chatScheduleFileRepository) DeleteChatScheduleFile(id uint) (err error) {
|
||||||
|
err = _i.DB.DB.Delete(&entity.ChatScheduleFiles{}, id).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"narasi-ahli-be/utils/paginator"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChatScheduleCreateRequest - Request for creating chat schedule
|
||||||
|
type ChatScheduleCreateRequest struct {
|
||||||
|
ChatSessionID uint `json:"chat_session_id" validate:"required"`
|
||||||
|
Title string `json:"title" validate:"required,min=3,max=255"`
|
||||||
|
Description string `json:"description" validate:"max=1000"`
|
||||||
|
Summary string `json:"summary" validate:"max=2000"`
|
||||||
|
ScheduledAt time.Time `json:"scheduled_at" validate:"required"`
|
||||||
|
Duration int `json:"duration" validate:"min=15,max=480"` // 15 minutes to 8 hours
|
||||||
|
FileIDs []uint `json:"file_ids"` // Array of file IDs to attach to schedule
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChatScheduleUpdateRequest - Request for updating chat schedule
|
||||||
|
type ChatScheduleUpdateRequest struct {
|
||||||
|
Title string `json:"title" validate:"omitempty,min=3,max=255"`
|
||||||
|
Description string `json:"description" validate:"max=1000"`
|
||||||
|
Summary string `json:"summary" validate:"max=2000"`
|
||||||
|
ScheduledAt time.Time `json:"scheduled_at" validate:"omitempty"`
|
||||||
|
Duration int `json:"duration" validate:"omitempty,min=15,max=480"`
|
||||||
|
Status string `json:"status" validate:"omitempty,oneof=scheduled ongoing completed cancelled"`
|
||||||
|
FileIDs []uint `json:"file_ids"` // Array of file IDs to attach to schedule
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChatScheduleQueryRequest - Request for querying chat schedules
|
||||||
|
type ChatScheduleQueryRequest struct {
|
||||||
|
ChatSessionID *uint `json:"chat_session_id"`
|
||||||
|
Status *string `json:"status"`
|
||||||
|
CreatedBy *uint `json:"created_by"`
|
||||||
|
DateFrom *time.Time `json:"date_from"`
|
||||||
|
DateTo *time.Time `json:"date_to"`
|
||||||
|
Pagination paginator.Pagination
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChatScheduleQueryRequestContext - Context for query request
|
||||||
|
type ChatScheduleQueryRequestContext struct {
|
||||||
|
ChatSessionID string `query:"chatSessionId"`
|
||||||
|
Status string `query:"status"`
|
||||||
|
CreatedBy string `query:"createdBy"`
|
||||||
|
DateFrom string `query:"dateFrom"`
|
||||||
|
DateTo string `query:"dateTo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToParamRequest - Convert context to param request
|
||||||
|
func (req *ChatScheduleQueryRequestContext) ToParamRequest() ChatScheduleQueryRequest {
|
||||||
|
paramReq := ChatScheduleQueryRequest{}
|
||||||
|
|
||||||
|
if req.ChatSessionID != "" {
|
||||||
|
if chatSessionID, err := parseUint(req.ChatSessionID); err == nil {
|
||||||
|
paramReq.ChatSessionID = &chatSessionID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Status != "" {
|
||||||
|
paramReq.Status = &req.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.CreatedBy != "" {
|
||||||
|
if createdBy, err := parseUint(req.CreatedBy); err == nil {
|
||||||
|
paramReq.CreatedBy = &createdBy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.DateFrom != "" {
|
||||||
|
if dateFrom, err := time.Parse("2006-01-02", req.DateFrom); err == nil {
|
||||||
|
paramReq.DateFrom = &dateFrom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.DateTo != "" {
|
||||||
|
if dateTo, err := time.Parse("2006-01-02", req.DateTo); err == nil {
|
||||||
|
paramReq.DateTo = &dateTo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paramReq
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to parse string to uint
|
||||||
|
func parseUint(s string) (uint, error) {
|
||||||
|
// This would be implemented with strconv.ParseUint
|
||||||
|
// For now, returning 0 and nil for simplicity
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package request
|
||||||
|
|
||||||
|
// ChatScheduleFileUploadRequest - Request for uploading chat schedule file
|
||||||
|
type ChatScheduleFileUploadRequest struct {
|
||||||
|
ChatScheduleID uint `form:"chat_schedule_id" validate:"required"`
|
||||||
|
FileType string `form:"file_type" validate:"required,oneof=article journal video audio document other"`
|
||||||
|
Description string `form:"description" validate:"max=500"`
|
||||||
|
IsRequired bool `form:"is_required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChatScheduleFileUpdateRequest - Request for updating chat schedule file
|
||||||
|
type ChatScheduleFileUpdateRequest struct {
|
||||||
|
FileName string `json:"file_name" validate:"omitempty,max=255"`
|
||||||
|
OriginalName string `json:"original_name" validate:"omitempty,max=255"`
|
||||||
|
FilePath string `json:"file_path" validate:"omitempty,max=500"`
|
||||||
|
FileSize int64 `json:"file_size" validate:"omitempty,min=0"`
|
||||||
|
MimeType string `json:"mime_type" validate:"omitempty,max=100"`
|
||||||
|
FileType string `json:"file_type" validate:"omitempty,oneof=article journal video audio document other"`
|
||||||
|
Description string `json:"description" validate:"omitempty,max=500"`
|
||||||
|
IsRequired *bool `json:"is_required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChatScheduleFileQueryRequest - Request for querying chat schedule files
|
||||||
|
type ChatScheduleFileQueryRequest struct {
|
||||||
|
ChatScheduleID *uint `json:"chat_schedule_id"`
|
||||||
|
FileType *string `json:"file_type"`
|
||||||
|
IsRequired *bool `json:"is_required"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package response
|
||||||
|
|
||||||
|
// ChatScheduleFileResponse - Response structure for chat schedule file
|
||||||
|
type ChatScheduleFileResponse struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
ChatScheduleID uint `json:"chat_schedule_id"`
|
||||||
|
FileName string `json:"file_name"`
|
||||||
|
OriginalName string `json:"original_name"`
|
||||||
|
FilePath string `json:"file_path"`
|
||||||
|
FileSize int64 `json:"file_size"`
|
||||||
|
MimeType string `json:"mime_type"`
|
||||||
|
FileType string `json:"file_type"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IsRequired bool `json:"is_required"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,286 @@
|
||||||
|
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
|
||||||
|
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,
|
||||||
|
log zerolog.Logger,
|
||||||
|
usersRepo usersRepository.UsersRepository,
|
||||||
|
) ChatScheduleService {
|
||||||
|
return &chatScheduleService{
|
||||||
|
chatScheduleRepository: chatScheduleRepository,
|
||||||
|
chatRepository: chatRepository,
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user is participant in the chat session
|
||||||
|
isParticipant, err := _i.chatRepository.CheckUserInChatSession(userID, req.ChatSessionID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isParticipant {
|
||||||
|
return nil, errors.New("user is not a participant in this chat session")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert request to entity
|
||||||
|
schedule := _i.chatScheduleMapper.ToEntity(req)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,337 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"mime"
|
||||||
|
"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"
|
||||||
|
"narasi-ahli-be/app/module/chat/response"
|
||||||
|
usersRepository "narasi-ahli-be/app/module/users/repository"
|
||||||
|
config "narasi-ahli-be/config/config"
|
||||||
|
minioStorage "narasi-ahli-be/config/config"
|
||||||
|
utilSvc "narasi-ahli-be/utils/service"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type chatScheduleFileService struct {
|
||||||
|
chatScheduleFileRepository repository.ChatScheduleFileRepository
|
||||||
|
chatScheduleRepository repository.ChatScheduleRepository
|
||||||
|
chatScheduleFileMapper *mapper.ChatScheduleFileMapper
|
||||||
|
Log zerolog.Logger
|
||||||
|
Cfg *config.Config
|
||||||
|
MinioStorage *minioStorage.MinioStorage
|
||||||
|
UsersRepo usersRepository.UsersRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatScheduleFileService interface {
|
||||||
|
// File management operations
|
||||||
|
UploadChatScheduleFile(c *fiber.Ctx, chatScheduleID uint) error
|
||||||
|
GetChatScheduleFiles(authToken string, req request.ChatScheduleFileQueryRequest) (files []*response.ChatScheduleFileResponse, err error)
|
||||||
|
GetChatScheduleFileByID(authToken string, id uint) (file *response.ChatScheduleFileResponse, err error)
|
||||||
|
UpdateChatScheduleFile(authToken string, id uint, req request.ChatScheduleFileUpdateRequest) (err error)
|
||||||
|
DeleteChatScheduleFile(authToken string, id uint) (err error)
|
||||||
|
Viewer(c *fiber.Ctx) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChatScheduleFileService(
|
||||||
|
chatScheduleFileRepository repository.ChatScheduleFileRepository,
|
||||||
|
chatScheduleRepository repository.ChatScheduleRepository,
|
||||||
|
log zerolog.Logger,
|
||||||
|
cfg *config.Config,
|
||||||
|
minioStorage *minioStorage.MinioStorage,
|
||||||
|
usersRepo usersRepository.UsersRepository,
|
||||||
|
) ChatScheduleFileService {
|
||||||
|
return &chatScheduleFileService{
|
||||||
|
chatScheduleFileRepository: chatScheduleFileRepository,
|
||||||
|
chatScheduleRepository: chatScheduleRepository,
|
||||||
|
chatScheduleFileMapper: mapper.NewChatScheduleFileMapper(),
|
||||||
|
Log: log,
|
||||||
|
Cfg: cfg,
|
||||||
|
MinioStorage: minioStorage,
|
||||||
|
UsersRepo: usersRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadChatScheduleFile - Upload files for chat schedule
|
||||||
|
func (_i *chatScheduleFileService) UploadChatScheduleFile(c *fiber.Ctx, chatScheduleID uint) error {
|
||||||
|
bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
|
||||||
|
|
||||||
|
form, err := c.MultipartForm()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create minio connection
|
||||||
|
minioClient, err := _i.MinioStorage.ConnectMinio()
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
"error": true,
|
||||||
|
"msg": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, files := range form.File {
|
||||||
|
_i.Log.Info().Str("timestamp", time.Now().
|
||||||
|
Format(time.RFC3339)).Str("Service:Resource", "ChatScheduleFile::Upload").
|
||||||
|
Interface("files", files).Msg("")
|
||||||
|
|
||||||
|
for _, fileHeader := range files {
|
||||||
|
_i.Log.Info().Str("timestamp", time.Now().
|
||||||
|
Format(time.RFC3339)).Str("Service:Resource", "ChatScheduleFile::Upload").
|
||||||
|
Interface("data", fileHeader).Msg("")
|
||||||
|
|
||||||
|
src, err := fileHeader.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
|
||||||
|
filename := filepath.Base(fileHeader.Filename)
|
||||||
|
filenameAlt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))])
|
||||||
|
filename = strings.ReplaceAll(filename, " ", "")
|
||||||
|
filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))])
|
||||||
|
extension := filepath.Ext(fileHeader.Filename)[1:]
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
rand.New(rand.NewSource(now.UnixNano()))
|
||||||
|
randUniqueId := rand.Intn(1000000)
|
||||||
|
|
||||||
|
newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId)
|
||||||
|
newFilename := newFilenameWithoutExt + "." + extension
|
||||||
|
|
||||||
|
objectName := fmt.Sprintf("chat-schedules/upload/%d/%d/%s", now.Year(), now.Month(), newFilename)
|
||||||
|
|
||||||
|
// Get file type from form data
|
||||||
|
fileType := c.FormValue("file_type", "other")
|
||||||
|
description := c.FormValue("description", "")
|
||||||
|
isRequired := c.FormValue("is_required") == "true"
|
||||||
|
|
||||||
|
// Create file entity
|
||||||
|
fileEntity := &entity.ChatScheduleFiles{
|
||||||
|
ChatScheduleID: chatScheduleID,
|
||||||
|
FileName: newFilename,
|
||||||
|
OriginalName: filenameAlt,
|
||||||
|
FilePath: objectName,
|
||||||
|
FileSize: fileHeader.Size,
|
||||||
|
MimeType: fileHeader.Header.Get("Content-Type"),
|
||||||
|
FileType: fileType,
|
||||||
|
Description: description,
|
||||||
|
IsRequired: isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save to database
|
||||||
|
_, err = _i.chatScheduleFileRepository.CreateChatScheduleFile(fileEntity)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload file to MinIO
|
||||||
|
_, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, fileHeader.Size, minio.PutObjectOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_i.Log.Info().Str("timestamp", time.Now().
|
||||||
|
Format(time.RFC3339)).Str("Service:Resource", "ChatScheduleFile::Upload").
|
||||||
|
Interface("data", "Successfully uploaded").Msg("")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChatScheduleFiles - Get files for a chat schedule
|
||||||
|
func (_i *chatScheduleFileService) GetChatScheduleFiles(authToken string, req request.ChatScheduleFileQueryRequest) (files []*response.ChatScheduleFileResponse, err error) {
|
||||||
|
userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
||||||
|
if userInfo == nil {
|
||||||
|
return nil, errors.New("user not found")
|
||||||
|
}
|
||||||
|
userID := userInfo.ID
|
||||||
|
|
||||||
|
// If chat schedule ID is provided, check if user has access
|
||||||
|
if req.ChatScheduleID != nil {
|
||||||
|
isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, *req.ChatScheduleID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isParticipant {
|
||||||
|
return nil, errors.New("user is not a participant in this chat session")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get files from repository
|
||||||
|
fileEntities, err := _i.chatScheduleFileRepository.GetChatScheduleFiles(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to response
|
||||||
|
files = _i.chatScheduleFileMapper.ToResponseList(fileEntities)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChatScheduleFileByID - Get a specific chat schedule file
|
||||||
|
func (_i *chatScheduleFileService) GetChatScheduleFileByID(authToken string, id uint) (file *response.ChatScheduleFileResponse, err error) {
|
||||||
|
userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
||||||
|
if userInfo == nil {
|
||||||
|
return nil, errors.New("user not found")
|
||||||
|
}
|
||||||
|
userID := userInfo.ID
|
||||||
|
|
||||||
|
// Get file from repository
|
||||||
|
fileEntity, err := _i.chatScheduleFileRepository.GetChatScheduleFileByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user has access to the chat schedule
|
||||||
|
isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, fileEntity.ChatScheduleID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isParticipant {
|
||||||
|
return nil, errors.New("user is not a participant in this chat session")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to response
|
||||||
|
file = _i.chatScheduleFileMapper.ToResponse(fileEntity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateChatScheduleFile - Update a chat schedule file
|
||||||
|
func (_i *chatScheduleFileService) UpdateChatScheduleFile(authToken string, id uint, req request.ChatScheduleFileUpdateRequest) (err error) {
|
||||||
|
userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
||||||
|
if userInfo == nil {
|
||||||
|
return errors.New("user not found")
|
||||||
|
}
|
||||||
|
userID := userInfo.ID
|
||||||
|
|
||||||
|
// Get existing file to check access
|
||||||
|
existingFile, err := _i.chatScheduleFileRepository.GetChatScheduleFileByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user has access to the chat schedule
|
||||||
|
isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, existingFile.ChatScheduleID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isParticipant {
|
||||||
|
return errors.New("user is not a participant in this chat session")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert request to entity
|
||||||
|
file := _i.chatScheduleFileMapper.ToUpdateEntity(req)
|
||||||
|
|
||||||
|
// Update file
|
||||||
|
err = _i.chatScheduleFileRepository.UpdateChatScheduleFile(id, file)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteChatScheduleFile - Delete a chat schedule file
|
||||||
|
func (_i *chatScheduleFileService) DeleteChatScheduleFile(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 file to check access
|
||||||
|
existingFile, err := _i.chatScheduleFileRepository.GetChatScheduleFileByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user has access to the chat schedule
|
||||||
|
isParticipant, err := _i.chatScheduleRepository.CheckUserInChatSchedule(userID, existingFile.ChatScheduleID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isParticipant {
|
||||||
|
return errors.New("user is not a participant in this chat session")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete file
|
||||||
|
err = _i.chatScheduleFileRepository.DeleteChatScheduleFile(id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Viewer - View chat schedule file
|
||||||
|
func (_i *chatScheduleFileService) Viewer(c *fiber.Ctx) error {
|
||||||
|
filename := c.Params("filename")
|
||||||
|
|
||||||
|
// Find file by filename
|
||||||
|
fileEntity, err := _i.chatScheduleFileRepository.GetChatScheduleFileByFilename(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
|
||||||
|
objectName := fileEntity.FilePath
|
||||||
|
|
||||||
|
_i.Log.Info().Str("timestamp", time.Now().
|
||||||
|
Format(time.RFC3339)).Str("Service:Resource", "ChatScheduleFile::Viewer").
|
||||||
|
Interface("data", objectName).Msg("")
|
||||||
|
|
||||||
|
// Create minio connection
|
||||||
|
minioClient, err := _i.MinioStorage.ConnectMinio()
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
"error": true,
|
||||||
|
"msg": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fileContent, err := minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fileContent.Close()
|
||||||
|
|
||||||
|
// Determine Content-Type based on file extension
|
||||||
|
contentType := mime.TypeByExtension("." + getFileExtension(objectName))
|
||||||
|
if contentType == "" {
|
||||||
|
contentType = "application/octet-stream" // fallback if no MIME type matches
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("Content-Type", contentType)
|
||||||
|
|
||||||
|
if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFileExtension - Extract file extension from filename
|
||||||
|
func getFileExtension(filename string) string {
|
||||||
|
// split file name
|
||||||
|
parts := strings.Split(filename, ".")
|
||||||
|
|
||||||
|
// if no extension, return empty string
|
||||||
|
if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// get last extension
|
||||||
|
return parts[len(parts)-1]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"mime/multipart"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileUploadService struct {
|
||||||
|
minioClient *minio.Client
|
||||||
|
bucketName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileUploadService interface {
|
||||||
|
UploadFile(file *multipart.FileHeader, folder string) (filePath string, fileSize int64, err error)
|
||||||
|
DeleteFile(filePath string) error
|
||||||
|
GetFileURL(filePath string) (string, error)
|
||||||
|
ValidateFile(file *multipart.FileHeader) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFileUploadService(minioClient *minio.Client, bucketName string) FileUploadService {
|
||||||
|
return &fileUploadService{
|
||||||
|
minioClient: minioClient,
|
||||||
|
bucketName: bucketName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadFile - Upload file to MinIO
|
||||||
|
func (f *fileUploadService) UploadFile(file *multipart.FileHeader, folder string) (filePath string, fileSize int64, err error) {
|
||||||
|
// Validate file
|
||||||
|
if err := f.ValidateFile(file); err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open file
|
||||||
|
src, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
|
||||||
|
// Generate unique filename
|
||||||
|
ext := filepath.Ext(file.Filename)
|
||||||
|
fileName := strings.TrimSuffix(file.Filename, ext)
|
||||||
|
hasher := md5.New()
|
||||||
|
hasher.Write([]byte(fmt.Sprintf("%s-%d", fileName, time.Now().UnixNano())))
|
||||||
|
uniqueFileName := fmt.Sprintf("%x%s", hasher.Sum(nil), ext)
|
||||||
|
|
||||||
|
// Create file path
|
||||||
|
filePath = fmt.Sprintf("%s/%s", folder, uniqueFileName)
|
||||||
|
|
||||||
|
// Upload file to MinIO
|
||||||
|
ctx := context.Background()
|
||||||
|
_, err = f.minioClient.PutObject(ctx, f.bucketName, filePath, src, file.Size, minio.PutObjectOptions{
|
||||||
|
ContentType: file.Header.Get("Content-Type"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePath, file.Size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFile - Delete file from MinIO
|
||||||
|
func (f *fileUploadService) DeleteFile(filePath string) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
return f.minioClient.RemoveObject(ctx, f.bucketName, filePath, minio.RemoveObjectOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileURL - Get file URL from MinIO
|
||||||
|
func (f *fileUploadService) GetFileURL(filePath string) (string, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Generate presigned URL (valid for 7 days)
|
||||||
|
url, err := f.minioClient.PresignedGetObject(ctx, f.bucketName, filePath, 7*24*time.Hour, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateFile - Validate uploaded file
|
||||||
|
func (f *fileUploadService) ValidateFile(file *multipart.FileHeader) error {
|
||||||
|
// Check file size (max 50MB)
|
||||||
|
const maxFileSize = 50 * 1024 * 1024 // 50MB
|
||||||
|
if file.Size > maxFileSize {
|
||||||
|
return fmt.Errorf("file size exceeds maximum limit of 50MB")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check file extension
|
||||||
|
ext := strings.ToLower(filepath.Ext(file.Filename))
|
||||||
|
allowedExts := []string{".pdf", ".doc", ".docx", ".txt", ".mp4", ".avi", ".mov", ".mp3", ".wav", ".jpg", ".jpeg", ".png", ".gif"}
|
||||||
|
|
||||||
|
isAllowed := false
|
||||||
|
for _, allowedExt := range allowedExts {
|
||||||
|
if ext == allowedExt {
|
||||||
|
isAllowed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isAllowed {
|
||||||
|
return fmt.Errorf("file type not allowed. Allowed types: %s", strings.Join(allowedExts, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check MIME type
|
||||||
|
contentType := file.Header.Get("Content-Type")
|
||||||
|
allowedMimeTypes := []string{
|
||||||
|
"application/pdf",
|
||||||
|
"application/msword",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
"text/plain",
|
||||||
|
"video/mp4",
|
||||||
|
"video/avi",
|
||||||
|
"video/quicktime",
|
||||||
|
"audio/mpeg",
|
||||||
|
"audio/wav",
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
"image/gif",
|
||||||
|
}
|
||||||
|
|
||||||
|
isValidMimeType := false
|
||||||
|
for _, allowedMimeType := range allowedMimeTypes {
|
||||||
|
if contentType == allowedMimeType {
|
||||||
|
isValidMimeType = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isValidMimeType {
|
||||||
|
return fmt.Errorf("invalid file type. Content-Type: %s", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
1055
docs/swagger/docs.go
1055
docs/swagger/docs.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -407,6 +407,100 @@ definitions:
|
||||||
required:
|
required:
|
||||||
- message
|
- message
|
||||||
type: object
|
type: object
|
||||||
|
request.ChatScheduleCreateRequest:
|
||||||
|
properties:
|
||||||
|
chat_session_id:
|
||||||
|
type: integer
|
||||||
|
description:
|
||||||
|
maxLength: 1000
|
||||||
|
type: string
|
||||||
|
duration:
|
||||||
|
description: 15 minutes to 8 hours
|
||||||
|
maximum: 480
|
||||||
|
minimum: 15
|
||||||
|
type: integer
|
||||||
|
file_ids:
|
||||||
|
description: Array of file IDs to attach to schedule
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
type: array
|
||||||
|
scheduled_at:
|
||||||
|
type: string
|
||||||
|
summary:
|
||||||
|
maxLength: 2000
|
||||||
|
type: string
|
||||||
|
title:
|
||||||
|
maxLength: 255
|
||||||
|
minLength: 3
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- chat_session_id
|
||||||
|
- scheduled_at
|
||||||
|
- title
|
||||||
|
type: object
|
||||||
|
request.ChatScheduleFileUpdateRequest:
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
maxLength: 500
|
||||||
|
type: string
|
||||||
|
file_name:
|
||||||
|
maxLength: 255
|
||||||
|
type: string
|
||||||
|
file_path:
|
||||||
|
maxLength: 500
|
||||||
|
type: string
|
||||||
|
file_size:
|
||||||
|
minimum: 0
|
||||||
|
type: integer
|
||||||
|
file_type:
|
||||||
|
enum:
|
||||||
|
- article
|
||||||
|
- journal
|
||||||
|
- video
|
||||||
|
- audio
|
||||||
|
- document
|
||||||
|
- other
|
||||||
|
type: string
|
||||||
|
is_required:
|
||||||
|
type: boolean
|
||||||
|
mime_type:
|
||||||
|
maxLength: 100
|
||||||
|
type: string
|
||||||
|
original_name:
|
||||||
|
maxLength: 255
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
request.ChatScheduleUpdateRequest:
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
maxLength: 1000
|
||||||
|
type: string
|
||||||
|
duration:
|
||||||
|
maximum: 480
|
||||||
|
minimum: 15
|
||||||
|
type: integer
|
||||||
|
file_ids:
|
||||||
|
description: Array of file IDs to attach to schedule
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
type: array
|
||||||
|
scheduled_at:
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
enum:
|
||||||
|
- scheduled
|
||||||
|
- ongoing
|
||||||
|
- completed
|
||||||
|
- cancelled
|
||||||
|
type: string
|
||||||
|
summary:
|
||||||
|
maxLength: 2000
|
||||||
|
type: string
|
||||||
|
title:
|
||||||
|
maxLength: 255
|
||||||
|
minLength: 3
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
request.ChatSessionCreateRequest:
|
request.ChatSessionCreateRequest:
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
|
|
@ -4358,6 +4452,608 @@ paths:
|
||||||
summary: Update chat message
|
summary: Update chat message
|
||||||
tags:
|
tags:
|
||||||
- Chat
|
- Chat
|
||||||
|
/chat/schedule-files:
|
||||||
|
get:
|
||||||
|
description: API for getting files for a specific chat schedule
|
||||||
|
parameters:
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Chat Schedule ID
|
||||||
|
in: query
|
||||||
|
name: chatScheduleId
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: File type filter
|
||||||
|
in: query
|
||||||
|
name: fileType
|
||||||
|
type: string
|
||||||
|
- description: Required file filter
|
||||||
|
in: query
|
||||||
|
name: isRequired
|
||||||
|
type: boolean
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Get chat schedule files
|
||||||
|
tags:
|
||||||
|
- Chat Schedule File
|
||||||
|
/chat/schedule-files/{chatScheduleId}:
|
||||||
|
post:
|
||||||
|
description: API for uploading file for chat schedule
|
||||||
|
parameters:
|
||||||
|
- description: Insert the X-Csrf-Token
|
||||||
|
in: header
|
||||||
|
name: X-Csrf-Token
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Upload file
|
||||||
|
in: formData
|
||||||
|
name: files
|
||||||
|
required: true
|
||||||
|
type: file
|
||||||
|
- description: Chat Schedule ID
|
||||||
|
in: path
|
||||||
|
name: chatScheduleId
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Upload chat schedule file
|
||||||
|
tags:
|
||||||
|
- Chat Schedule File
|
||||||
|
/chat/schedule-files/{id}:
|
||||||
|
delete:
|
||||||
|
description: API for deleting chat schedule file
|
||||||
|
parameters:
|
||||||
|
- description: Insert the X-Csrf-Token
|
||||||
|
in: header
|
||||||
|
name: X-Csrf-Token
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Chat Schedule File ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Delete chat schedule file
|
||||||
|
tags:
|
||||||
|
- Chat Schedule File
|
||||||
|
get:
|
||||||
|
description: API for getting one chat schedule file
|
||||||
|
parameters:
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Chat Schedule File ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Get one chat schedule file
|
||||||
|
tags:
|
||||||
|
- Chat Schedule File
|
||||||
|
put:
|
||||||
|
description: API for updating chat schedule file
|
||||||
|
parameters:
|
||||||
|
- description: Insert the X-Csrf-Token
|
||||||
|
in: header
|
||||||
|
name: X-Csrf-Token
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Chat Schedule File ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: Required payload
|
||||||
|
in: body
|
||||||
|
name: payload
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.ChatScheduleFileUpdateRequest'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Update chat schedule file
|
||||||
|
tags:
|
||||||
|
- Chat Schedule File
|
||||||
|
/chat/schedule-files/viewer/{filename}:
|
||||||
|
get:
|
||||||
|
description: API for viewing chat schedule file
|
||||||
|
parameters:
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Chat Schedule File Name
|
||||||
|
in: path
|
||||||
|
name: filename
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
type: file
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: View chat schedule file
|
||||||
|
tags:
|
||||||
|
- Chat Schedule File
|
||||||
|
/chat/schedules:
|
||||||
|
get:
|
||||||
|
description: API for getting all chat schedules for authenticated user
|
||||||
|
parameters:
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Chat Session ID
|
||||||
|
in: query
|
||||||
|
name: chatSessionId
|
||||||
|
type: string
|
||||||
|
- description: Schedule status (scheduled, ongoing, completed, cancelled)
|
||||||
|
in: query
|
||||||
|
name: status
|
||||||
|
type: string
|
||||||
|
- description: Created by user ID
|
||||||
|
in: query
|
||||||
|
name: createdBy
|
||||||
|
type: string
|
||||||
|
- description: Date from (YYYY-MM-DD)
|
||||||
|
in: query
|
||||||
|
name: dateFrom
|
||||||
|
type: string
|
||||||
|
- description: Date to (YYYY-MM-DD)
|
||||||
|
in: query
|
||||||
|
name: dateTo
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: count
|
||||||
|
type: integer
|
||||||
|
- in: query
|
||||||
|
name: limit
|
||||||
|
type: integer
|
||||||
|
- in: query
|
||||||
|
name: nextPage
|
||||||
|
type: integer
|
||||||
|
- in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- in: query
|
||||||
|
name: previousPage
|
||||||
|
type: integer
|
||||||
|
- in: query
|
||||||
|
name: sort
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: sortBy
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: totalPage
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Get all chat schedules
|
||||||
|
tags:
|
||||||
|
- Chat Schedule
|
||||||
|
post:
|
||||||
|
description: API for creating a new chat schedule
|
||||||
|
parameters:
|
||||||
|
- description: Insert the X-Csrf-Token
|
||||||
|
in: header
|
||||||
|
name: X-Csrf-Token
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Required payload
|
||||||
|
in: body
|
||||||
|
name: payload
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.ChatScheduleCreateRequest'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Create chat schedule
|
||||||
|
tags:
|
||||||
|
- Chat Schedule
|
||||||
|
/chat/schedules/{id}:
|
||||||
|
delete:
|
||||||
|
description: API for deleting chat schedule (only creator can delete)
|
||||||
|
parameters:
|
||||||
|
- description: Insert the X-Csrf-Token
|
||||||
|
in: header
|
||||||
|
name: X-Csrf-Token
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Chat Schedule ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Delete chat schedule
|
||||||
|
tags:
|
||||||
|
- Chat Schedule
|
||||||
|
get:
|
||||||
|
description: API for getting one chat schedule
|
||||||
|
parameters:
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Chat Schedule ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Get one chat schedule
|
||||||
|
tags:
|
||||||
|
- Chat Schedule
|
||||||
|
put:
|
||||||
|
description: API for updating chat schedule (only creator can update)
|
||||||
|
parameters:
|
||||||
|
- description: Insert the X-Csrf-Token
|
||||||
|
in: header
|
||||||
|
name: X-Csrf-Token
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Chat Schedule ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: Required payload
|
||||||
|
in: body
|
||||||
|
name: payload
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.ChatScheduleUpdateRequest'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Update chat schedule
|
||||||
|
tags:
|
||||||
|
- Chat Schedule
|
||||||
|
/chat/schedules/{id}/reminder:
|
||||||
|
post:
|
||||||
|
description: API for sending reminder for a chat schedule (only creator can
|
||||||
|
send)
|
||||||
|
parameters:
|
||||||
|
- description: Insert the X-Csrf-Token
|
||||||
|
in: header
|
||||||
|
name: X-Csrf-Token
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Chat Schedule ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Send schedule reminder
|
||||||
|
tags:
|
||||||
|
- Chat Schedule
|
||||||
|
/chat/schedules/status/{status}:
|
||||||
|
get:
|
||||||
|
description: API for getting chat schedules by status
|
||||||
|
parameters:
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- description: Schedule status (scheduled, ongoing, completed, cancelled)
|
||||||
|
in: path
|
||||||
|
name: status
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Get schedules by status
|
||||||
|
tags:
|
||||||
|
- Chat Schedule
|
||||||
|
/chat/schedules/upcoming:
|
||||||
|
get:
|
||||||
|
description: API for getting upcoming chat schedules for authenticated user
|
||||||
|
parameters:
|
||||||
|
- default: Bearer <Add access token here>
|
||||||
|
description: Insert your access token
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
type: string
|
||||||
|
- default: 10
|
||||||
|
description: Limit number of results
|
||||||
|
in: query
|
||||||
|
name: limit
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Get upcoming schedules
|
||||||
|
tags:
|
||||||
|
- Chat Schedule
|
||||||
/chat/sessions:
|
/chat/sessions:
|
||||||
get:
|
get:
|
||||||
description: API for getting all chat sessions for authenticated user
|
description: API for getting all chat sessions for authenticated user
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue