Add CMS Management Functionality
- Introduced a new CMS management feature, including the ability to create, retrieve, update, search, and delete CMS entries. - Implemented the CMS entity, repository, service, and handler layers following Clean Architecture principles. - Added API endpoints for CMS operations in Swagger documentation, ensuring comprehensive API specifications. - Enhanced error handling and validation for CMS data, improving robustness and user experience. - Updated the main application to initialize the CMS repository and service, integrating them into the existing system.
This commit is contained in:
@@ -0,0 +1,328 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"tm/pkg/logger"
|
||||
orm "tm/pkg/mongo"
|
||||
"tm/pkg/response"
|
||||
)
|
||||
|
||||
// Service defines the interface for CMS business operations
|
||||
type Service interface {
|
||||
// Create creates a new CMS entry
|
||||
Create(ctx context.Context, form *CMSForm) (*CMSResponse, error)
|
||||
|
||||
// GetByID retrieves a CMS entry by ID
|
||||
GetByID(ctx context.Context, id string) (*CMSResponse, error)
|
||||
|
||||
// GetByKey retrieves a CMS entry by key
|
||||
GetByKey(ctx context.Context, key string) (*CMSResponse, error)
|
||||
|
||||
// Update updates a CMS entry
|
||||
Update(ctx context.Context, id string, form *CMSForm) (*CMSResponse, error)
|
||||
|
||||
// Delete deletes a CMS entry by ID
|
||||
Delete(ctx context.Context, id string) error
|
||||
|
||||
// Search searches CMS entries with filters and pagination
|
||||
Search(ctx context.Context, form *SearchCMSForm, pagination *response.Pagination) (*CMSListResponse, error)
|
||||
}
|
||||
|
||||
// cmsService implements Service interface
|
||||
type cmsService struct {
|
||||
repo Repository
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewService creates a new CMS service
|
||||
func NewService(repo Repository, logger logger.Logger) Service {
|
||||
return &cmsService{
|
||||
repo: repo,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Create creates a new CMS entry
|
||||
func (s *cmsService) Create(ctx context.Context, form *CMSForm) (*CMSResponse, error) {
|
||||
s.logger.Info("Creating new CMS entry", map[string]interface{}{
|
||||
"key": form.Key,
|
||||
})
|
||||
|
||||
// Check if CMS with this key already exists
|
||||
existing, err := s.repo.GetByKey(ctx, form.Key)
|
||||
if err == nil && existing != nil {
|
||||
s.logger.Warn("CMS with this key already exists", map[string]interface{}{
|
||||
"key": form.Key,
|
||||
})
|
||||
return nil, ErrCMSKeyExists
|
||||
}
|
||||
|
||||
// Create CMS entity
|
||||
cms := &CMS{
|
||||
Key: form.Key,
|
||||
}
|
||||
|
||||
// Copy form data to entity
|
||||
s.copyFormToEntity(form, cms)
|
||||
|
||||
// Save to database
|
||||
if err := s.repo.Create(ctx, cms); err != nil {
|
||||
s.logger.Error("Failed to create CMS", map[string]interface{}{
|
||||
"key": form.Key,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, fmt.Errorf("failed to create CMS: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("CMS entry created successfully", map[string]interface{}{
|
||||
"id": cms.GetID(),
|
||||
"key": form.Key,
|
||||
})
|
||||
|
||||
return cms.ToResponse(), nil
|
||||
}
|
||||
|
||||
// GetByID retrieves a CMS entry by ID
|
||||
func (s *cmsService) GetByID(ctx context.Context, id string) (*CMSResponse, error) {
|
||||
s.logger.Debug("Retrieving CMS by ID", map[string]interface{}{
|
||||
"id": id,
|
||||
})
|
||||
|
||||
cms, err := s.repo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get CMS by ID", map[string]interface{}{
|
||||
"id": id,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, fmt.Errorf("failed to get CMS: %w", err)
|
||||
}
|
||||
|
||||
return cms.ToResponse(), nil
|
||||
}
|
||||
|
||||
// GetByKey retrieves a CMS entry by key
|
||||
func (s *cmsService) GetByKey(ctx context.Context, key string) (*CMSResponse, error) {
|
||||
s.logger.Debug("Retrieving CMS by key", map[string]interface{}{
|
||||
"key": key,
|
||||
})
|
||||
|
||||
cms, err := s.repo.GetByKey(ctx, key)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get CMS by key", map[string]interface{}{
|
||||
"key": key,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, fmt.Errorf("failed to get CMS: %w", err)
|
||||
}
|
||||
|
||||
return cms.ToResponse(), nil
|
||||
}
|
||||
|
||||
// Update updates a CMS entry
|
||||
func (s *cmsService) Update(ctx context.Context, id string, form *CMSForm) (*CMSResponse, error) {
|
||||
s.logger.Info("Updating CMS entry", map[string]interface{}{
|
||||
"id": id,
|
||||
"key": form.Key,
|
||||
})
|
||||
|
||||
// Get existing CMS
|
||||
cms, err := s.repo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get existing CMS for update", map[string]interface{}{
|
||||
"id": id,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, fmt.Errorf("failed to get CMS for update: %w", err)
|
||||
}
|
||||
|
||||
// Check if key is being changed and if new key already exists
|
||||
if cms.Key != form.Key {
|
||||
existing, err := s.repo.GetByKey(ctx, form.Key)
|
||||
if err == nil && existing != nil && existing.GetID() != id {
|
||||
s.logger.Warn("CMS with this key already exists", map[string]interface{}{
|
||||
"key": form.Key,
|
||||
})
|
||||
return nil, ErrCMSKeyExists
|
||||
}
|
||||
cms.Key = form.Key
|
||||
}
|
||||
|
||||
// Copy form data to entity
|
||||
s.copyFormToEntity(form, cms)
|
||||
|
||||
// Update in database
|
||||
if err := s.repo.Update(ctx, cms); err != nil {
|
||||
s.logger.Error("Failed to update CMS", map[string]interface{}{
|
||||
"id": id,
|
||||
"key": form.Key,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, fmt.Errorf("failed to update CMS: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("CMS entry updated successfully", map[string]interface{}{
|
||||
"id": id,
|
||||
"key": form.Key,
|
||||
})
|
||||
|
||||
return cms.ToResponse(), nil
|
||||
}
|
||||
|
||||
// Delete deletes a CMS entry by ID
|
||||
func (s *cmsService) Delete(ctx context.Context, id string) error {
|
||||
s.logger.Info("Deleting CMS entry", map[string]interface{}{
|
||||
"id": id,
|
||||
})
|
||||
|
||||
if err := s.repo.Delete(ctx, id); err != nil {
|
||||
s.logger.Error("Failed to delete CMS", map[string]interface{}{
|
||||
"id": id,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return fmt.Errorf("failed to delete CMS: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("CMS entry deleted successfully", map[string]interface{}{
|
||||
"id": id,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Search searches CMS entries with filters and pagination
|
||||
func (s *cmsService) Search(ctx context.Context, form *SearchCMSForm, pagination *response.Pagination) (*CMSListResponse, error) {
|
||||
s.logger.Debug("Searching CMS entries", map[string]interface{}{
|
||||
"filters": form,
|
||||
"pagination": pagination,
|
||||
})
|
||||
|
||||
// Convert response.Pagination to orm.Pagination
|
||||
ormPagination := orm.Pagination{
|
||||
Limit: pagination.Limit,
|
||||
Skip: pagination.Offset,
|
||||
SortField: pagination.SortBy,
|
||||
SortOrder: 1, // Default ascending
|
||||
}
|
||||
if pagination.SortOrder == "desc" {
|
||||
ormPagination.SortOrder = -1
|
||||
}
|
||||
|
||||
result, err := s.repo.Search(ctx, form, ormPagination)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to search CMS entries", map[string]interface{}{
|
||||
"filters": form,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, fmt.Errorf("failed to search CMS entries: %w", err)
|
||||
}
|
||||
|
||||
// Convert to response format
|
||||
cmsResponses := make([]*CMSResponse, len(result.Items))
|
||||
for i, cms := range result.Items {
|
||||
cmsResponses[i] = cms.ToResponse()
|
||||
}
|
||||
|
||||
s.logger.Info("CMS search completed", map[string]interface{}{
|
||||
"total_count": result.TotalCount,
|
||||
"items_count": len(result.Items),
|
||||
})
|
||||
|
||||
return &CMSListResponse{
|
||||
CMS: cmsResponses,
|
||||
Meta: pagination.Response(result.TotalCount),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// copyFormToEntity copies data from form to entity
|
||||
func (s *cmsService) copyFormToEntity(form *CMSForm, cms *CMS) {
|
||||
// Hero section
|
||||
if form.Hero.Title != "" {
|
||||
cms.Hero.Title = form.Hero.Title
|
||||
}
|
||||
if form.Hero.Description != "" {
|
||||
cms.Hero.Description = form.Hero.Description
|
||||
}
|
||||
if form.Hero.ButtonText != "" {
|
||||
cms.Hero.ButtonText = form.Hero.ButtonText
|
||||
}
|
||||
if form.Hero.ButtonLink != "" {
|
||||
cms.Hero.ButtonLink = form.Hero.ButtonLink
|
||||
}
|
||||
if form.Hero.GifFile != "" {
|
||||
cms.Hero.GifFile = form.Hero.GifFile
|
||||
}
|
||||
|
||||
// Chart section
|
||||
if form.Chart.Title != "" {
|
||||
cms.Chart.Title = form.Chart.Title
|
||||
}
|
||||
if form.Chart.MissedAmount != "" {
|
||||
cms.Chart.MissedAmount = form.Chart.MissedAmount
|
||||
}
|
||||
if len(form.Chart.Data) > 0 {
|
||||
cms.Chart.Data = make([]chartPoint, len(form.Chart.Data))
|
||||
for i, point := range form.Chart.Data {
|
||||
cms.Chart.Data[i] = chartPoint(point)
|
||||
}
|
||||
}
|
||||
|
||||
// Features section
|
||||
s.copyCardSectionForm(&form.Features, &cms.Features)
|
||||
|
||||
// Challenges section
|
||||
s.copyCardSectionForm(&form.Challenges, &cms.Challenges)
|
||||
|
||||
// Advantages section
|
||||
s.copyCardSectionForm(&form.Advantages, &cms.Advantages)
|
||||
|
||||
// Contact section
|
||||
if form.Contact.Title != "" {
|
||||
cms.Contact.Title = form.Contact.Title
|
||||
}
|
||||
if form.Contact.Description != "" {
|
||||
cms.Contact.Description = form.Contact.Description
|
||||
}
|
||||
if form.Contact.SubmitButtonText != "" {
|
||||
cms.Contact.SubmitButtonText = form.Contact.SubmitButtonText
|
||||
}
|
||||
if len(form.Contact.Fields) > 0 {
|
||||
cms.Contact.Fields = make([]formField, len(form.Contact.Fields))
|
||||
for i, field := range form.Contact.Fields {
|
||||
cms.Contact.Fields[i] = formField(field)
|
||||
}
|
||||
}
|
||||
|
||||
// Footer section
|
||||
if form.Footer.Email != "" {
|
||||
cms.Footer.Email = form.Footer.Email
|
||||
}
|
||||
if form.Footer.Phone != "" {
|
||||
cms.Footer.Phone = form.Footer.Phone
|
||||
}
|
||||
if form.Footer.Location != "" {
|
||||
cms.Footer.Location = form.Footer.Location
|
||||
}
|
||||
if form.Footer.Tagline != "" {
|
||||
cms.Footer.Tagline = form.Footer.Tagline
|
||||
}
|
||||
if form.Footer.Copyright != "" {
|
||||
cms.Footer.Copyright = form.Footer.Copyright
|
||||
}
|
||||
}
|
||||
|
||||
// copyCardSectionForm copies card section form data to entity
|
||||
func (s *cmsService) copyCardSectionForm(formSection *cardSectionForm, entitySection *cardSection) {
|
||||
if formSection.Title != "" {
|
||||
entitySection.Title = formSection.Title
|
||||
}
|
||||
if formSection.Description != "" {
|
||||
entitySection.Description = formSection.Description
|
||||
}
|
||||
if len(formSection.Cards) > 0 {
|
||||
entitySection.Cards = make([]card, len(formSection.Cards))
|
||||
for i, cardForm := range formSection.Cards {
|
||||
entitySection.Cards[i] = card(cardForm)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user