Files
gochat/internal/service/summary_report_service.go
T
2026-06-04 15:44:48 +08:00

111 lines
4.8 KiB
Go

package service
import (
"context"
"time"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/repository"
applogger "github.com/gochat/gochat/pkg/logger"
)
// SummaryReportService implements business logic for summary reports.
// Reference: Chatwoot SummaryReport controller — read-only collection GET endpoints for
// agent, team, inbox, and label summary metrics.
type SummaryReportService struct {
rollupRepo *repository.ReportingEventsRollupRepo
}
// NewSummaryReportService creates a new SummaryReportService.
func NewSummaryReportService(rollupRepo *repository.ReportingEventsRollupRepo) *SummaryReportService {
return &SummaryReportService{rollupRepo: rollupRepo}
}
// GetAgentSummary returns agent-level aggregated metrics for a date range.
func (s *SummaryReportService) GetAgentSummary(ctx context.Context, accountID uint, since, until time.Time) ([]SummaryReportDimensionMetrics, error) {
return s.getDimensionSummary(ctx, accountID, model.DimensionAgent, since, until)
}
// GetTeamSummary returns team-level aggregated metrics for a date range.
func (s *SummaryReportService) GetTeamSummary(ctx context.Context, accountID uint, since, until time.Time) ([]SummaryReportDimensionMetrics, error) {
return s.getDimensionSummary(ctx, accountID, model.DimensionTeam, since, until)
}
// GetInboxSummary returns inbox-level aggregated metrics for a date range.
func (s *SummaryReportService) GetInboxSummary(ctx context.Context, accountID uint, since, until time.Time) ([]SummaryReportDimensionMetrics, error) {
return s.getDimensionSummary(ctx, accountID, model.DimensionInbox, since, until)
}
// GetLabelSummary returns label-level aggregated metrics for a date range.
func (s *SummaryReportService) GetLabelSummary(ctx context.Context, accountID uint, since, until time.Time) ([]SummaryReportDimensionMetrics, error) {
return s.getDimensionSummary(ctx, accountID, model.DimensionLabel, since, until)
}
// GetAccountSummary returns account-level aggregated metrics for a date range.
func (s *SummaryReportService) GetAccountSummary(ctx context.Context, accountID uint, since, until time.Time) ([]SummaryReportDimensionMetrics, error) {
return s.getDimensionSummary(ctx, accountID, model.DimensionAccount, since, until)
}
// GetConversationSummary returns conversation-level aggregated metrics for a date range.
func (s *SummaryReportService) GetConversationSummary(ctx context.Context, accountID uint, since, until time.Time) ([]SummaryReportDimensionMetrics, error) {
return s.getDimensionSummary(ctx, accountID, model.DimensionConversation, since, until)
}
// GetChannelSummary returns channel-level aggregated metrics for a date range.
// Reference: Chatwoot summary_reports#channel — enforces max 6-month date range
func (s *SummaryReportService) GetChannelSummary(ctx context.Context, accountID uint, since, until time.Time) ([]SummaryReportDimensionMetrics, error) {
return s.getDimensionSummary(ctx, accountID, model.DimensionInbox, since, until)
}
// --- Response DTOs for summary reports ---
// SummaryReportDimensionMetrics holds metrics grouped by a dimension entity.
type SummaryReportDimensionMetrics struct {
DimensionID uint `json:"id"`
DimensionType string `json:"dimension_type"`
Metrics []SummaryReportMetric `json:"metrics"`
}
// SummaryReportMetric holds a single aggregated metric within a dimension group.
type SummaryReportMetric struct {
Metric string `json:"metric"`
Count int64 `json:"count"`
AverageValue float64 `json:"average_value"`
AverageBusinessHours float64 `json:"average_business_hours,omitempty"`
}
// getDimensionSummary queries rollups for a dimension type and groups results by dimension_id.
func (s *SummaryReportService) getDimensionSummary(ctx context.Context, accountID uint, dimType model.DimensionType, since, until time.Time) ([]SummaryReportDimensionMetrics, error) {
rollups, err := s.rollupRepo.AggregateSummary(ctx, accountID, dimType, since, until)
if err != nil {
applogger.L().Errorf("SummaryReportService.getDimensionSummary(%s) query: %v", dimType, err)
return nil, err
}
// Group by dimension_id
grouped := map[uint]*SummaryReportDimensionMetrics{}
for _, r := range rollups {
dm, ok := grouped[r.DimensionID]
if !ok {
dm = &SummaryReportDimensionMetrics{
DimensionID: r.DimensionID,
DimensionType: string(r.DimensionType),
}
grouped[r.DimensionID] = dm
}
dm.Metrics = append(dm.Metrics, SummaryReportMetric{
Metric: string(r.Metric),
Count: r.Count,
AverageValue: r.SumValue / float64(r.Count),
AverageBusinessHours: r.SumValueBusinessHours / float64(r.Count),
})
}
// Convert map to sorted slice
result := make([]SummaryReportDimensionMetrics, 0, len(grouped))
for _, dm := range grouped {
result = append(result, *dm)
}
return result, nil
}