package appeal import ( "time" "api_yal/internal/models" ) // CreateAppealRequest DTO для создания обращения type CreateAppealRequest struct { Type string `json:"type" validate:"required,oneof=complaint suggestion wish question other"` Title string `json:"title" validate:"required,min=3,max=255"` Message string `json:"message" validate:"required,min=10"` Priority string `json:"priority" validate:"omitempty,oneof=low medium high critical"` ObjectID *uint `json:"object_id,omitempty"` FeedbackID *uint `json:"feedback_id,omitempty"` CommentID *uint `json:"comment_id,omitempty"` ContactName string `json:"contact_name,omitempty"` ContactEmail string `json:"contact_email,omitempty" validate:"omitempty,email"` ContactPhone string `json:"contact_phone,omitempty"` Attachments []string `json:"attachments,omitempty"` Category string `json:"category,omitempty"` Labels []string `json:"labels,omitempty"` CustomData map[string]interface{} `json:"custom_data,omitempty"` IPAddress string `json:"ip_address,omitempty"` UserAgent string `json:"user_agent,omitempty"` } // UpdateAppealRequest DTO для обновления обращения type UpdateAppealRequest struct { Title *string `json:"title,omitempty" validate:"omitempty,min=3,max=255"` Message *string `json:"message,omitempty" validate:"omitempty,min=10"` Priority *string `json:"priority,omitempty" validate:"omitempty,oneof=low medium high critical"` Category *string `json:"category,omitempty"` Labels []string `json:"labels,omitempty"` Attachments []string `json:"attachments,omitempty"` CustomData map[string]interface{} `json:"custom_data,omitempty"` } // UpdateStatusRequest DTO для обновления статуса type UpdateStatusRequest struct { Status string `json:"status" validate:"required,oneof=new in_progress resolved rejected closed"` Comment string `json:"comment,omitempty"` } // AssignRequest DTO для назначения ответственного type AssignRequest struct { AssignedToID *uint `json:"assigned_to_id"` } // ResolveRequest DTO для решения обращения type ResolveRequest struct { Resolution string `json:"resolution" validate:"required,min=5"` } // AppealResponse DTO для ответа type AppealResponse struct { ID uint `json:"id"` Type string `json:"type"` Status string `json:"status"` Priority string `json:"priority"` Title string `json:"title"` Message string `json:"message"` AuthorID *uint `json:"author_id,omitempty"` Author *AuthorInfo `json:"author,omitempty"` ObjectID *uint `json:"object_id,omitempty"` Object *ObjectInfo `json:"object,omitempty"` FeedbackID *uint `json:"feedback_id,omitempty"` CommentID *uint `json:"comment_id,omitempty"` ContactName string `json:"contact_name,omitempty"` ContactEmail string `json:"contact_email,omitempty"` ContactPhone string `json:"contact_phone,omitempty"` Attachments []string `json:"attachments,omitempty"` Category string `json:"category,omitempty"` Labels []string `json:"labels,omitempty"` AssignedToID *uint `json:"assigned_to_id,omitempty"` AssignedTo *AuthorInfo `json:"assigned_to,omitempty"` ResolvedAt *time.Time `json:"resolved_at,omitempty"` ResolvedBy *uint `json:"resolved_by,omitempty"` Resolution string `json:"resolution,omitempty"` CustomData map[string]interface{} `json:"custom_data,omitempty"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // AuthorInfo информация об авторе type AuthorInfo struct { ID uint `json:"id"` Email string `json:"email"` FirstName string `json:"first_name"` LastName string `json:"last_name"` } // ObjectInfo информация об объекте type ObjectInfo struct { ID uint `json:"id"` Name string `json:"name"` Description string `json:"description"` } // ListAppealsResponse DTO для списка обращений type ListAppealsResponse struct { Data []AppealResponse `json:"data"` Total int64 `json:"total"` Offset int `json:"offset"` Limit int `json:"limit"` TotalPages int `json:"total_pages"` } // AppealHistoryResponse DTO для истории type AppealHistoryResponse struct { ID uint `json:"id"` AppealID uint `json:"appeal_id"` UserID *uint `json:"user_id,omitempty"` User *AuthorInfo `json:"user,omitempty"` OldStatus string `json:"old_status"` NewStatus string `json:"new_status"` Comment string `json:"comment,omitempty"` CreatedAt time.Time `json:"created_at"` } // ListHistoryResponse DTO для списка истории type ListHistoryResponse struct { Data []AppealHistoryResponse `json:"data"` Total int64 `json:"total"` Offset int `json:"offset"` Limit int `json:"limit"` TotalPages int `json:"total_pages"` } // AppealStatisticsResponse DTO для статистики type AppealStatisticsResponse struct { Total int64 `json:"total"` ByStatus map[string]int64 `json:"by_status"` ByType map[string]int64 `json:"by_type"` ByPriority map[string]int64 `json:"by_priority"` ByCategory map[string]int64 `json:"by_category,omitempty"` AvgResolveTime float64 `json:"avg_resolve_time_hours"` } // NewAppealResponse создает AppealResponse из модели func NewAppealResponse(appeal *models.Appeal) AppealResponse { resp := AppealResponse{ ID: appeal.ID, Type: string(appeal.Type), Status: string(appeal.Status), Priority: string(appeal.Priority), Title: appeal.Title, Message: appeal.Message, AuthorID: appeal.AuthorID, ObjectID: appeal.ObjectID, FeedbackID: appeal.FeedbackID, CommentID: appeal.CommentID, ContactName: appeal.ContactName, ContactEmail: appeal.ContactEmail, ContactPhone: appeal.ContactPhone, Attachments: appeal.Attachments, Category: appeal.Category, Labels: appeal.Labels, AssignedToID: appeal.AssignedToID, ResolvedAt: appeal.ResolvedAt, ResolvedBy: appeal.ResolvedBy, Resolution: appeal.Resolution, CustomData: appeal.CustomData, CreatedAt: appeal.CreatedAt, UpdatedAt: appeal.UpdatedAt, } if appeal.Author != nil { resp.Author = &AuthorInfo{ ID: appeal.Author.ID, Email: appeal.Author.Email, FirstName: appeal.Author.FirstName, LastName: appeal.Author.LastName, } } if appeal.Object != nil { resp.Object = &ObjectInfo{ ID: appeal.Object.ID, Name: appeal.Object.ShortName, Description: appeal.Object.Description, } } if appeal.AssignedTo != nil { resp.AssignedTo = &AuthorInfo{ ID: appeal.AssignedTo.ID, Email: appeal.AssignedTo.Email, FirstName: appeal.AssignedTo.FirstName, LastName: appeal.AssignedTo.LastName, } } return resp } // NewAppealHistoryResponse создает AppealHistoryResponse из модели func NewAppealHistoryResponse(history *models.AppealHistory) AppealHistoryResponse { resp := AppealHistoryResponse{ ID: history.ID, AppealID: history.AppealID, UserID: history.UserID, OldStatus: string(history.OldStatus), NewStatus: string(history.NewStatus), Comment: history.Comment, CreatedAt: history.CreatedAt, } if history.User != nil { resp.User = &AuthorInfo{ ID: history.User.ID, Email: history.User.Email, FirstName: history.User.FirstName, LastName: history.User.LastName, } } return resp }