From 490aa19840c489f3ba1eeac766043ea000ea06b0 Mon Sep 17 00:00:00 2001 From: Efril Date: Tue, 27 Jan 2026 00:38:32 +0700 Subject: [PATCH] update analytic --- internal/repository/analytics_repository.go | 178 ++++++++++++++------ 1 file changed, 122 insertions(+), 56 deletions(-) diff --git a/internal/repository/analytics_repository.go b/internal/repository/analytics_repository.go index 53f6fab..e5712fe 100644 --- a/internal/repository/analytics_repository.go +++ b/internal/repository/analytics_repository.go @@ -3,6 +3,8 @@ package repository import ( "context" "fmt" + "log" + "strings" "time" "github.com/google/uuid" @@ -320,79 +322,143 @@ func (r *AnalyticsRepository) GetDepartmentStats(ctx context.Context, startDate, db := DBFromContext(ctx, r.db) var results []map[string]interface{} - // First try using summary table for better performance - query := ` - SELECT - d.id as department_id, - d.name as department_name, - d.code as department_code, - COALESCE(SUM(dls.incoming_count), 0) as incoming_count, - COALESCE(SUM(dls.outgoing_count), 0) as outgoing_count, - COALESCE(SUM(dls.pending_outgoing), 0) as pending_count, - COALESCE(AVG(dls.avg_response_hours), 0) as avg_response_time, - COALESCE(AVG(dls.completion_rate), 0) as completion_rate - FROM departments d - LEFT JOIN department_letter_summary dls ON dls.department_id = d.id - WHERE 1=1 - %s - GROUP BY d.id, d.name, d.code - ORDER BY (COALESCE(SUM(dls.incoming_count), 0) + COALESCE(SUM(dls.outgoing_count), 0)) DESC - ` + // Format tanggal untuk logging + log.Printf("GetDepartmentStats called with startDate: %v, endDate: %v", startDate, endDate) + + // Try summary table first + query := ` + SELECT + d.id as department_id, + d.name as department_name, + d.code as department_code, + COALESCE(SUM(dls.incoming_count), 0) as incoming_count, + COALESCE(SUM(dls.outgoing_count), 0) as outgoing_count, + COALESCE(SUM(dls.pending_outgoing), 0) as pending_count, + COALESCE(AVG(dls.avg_response_hours), 0) as avg_response_time, + COALESCE(AVG(dls.completion_rate), 0) as completion_rate + FROM departments d + LEFT JOIN department_letter_summary dls ON dls.department_id = d.id` + + var conditions []string + var args []interface{} - dateFilter := "" if !startDate.IsZero() { - dateFilter += fmt.Sprintf(" AND dls.summary_date >= '%s'", startDate.Format("2006-01-02")) + conditions = append(conditions, "dls.summary_date >= ?") + args = append(args, startDate.Format("2006-01-02")) } if !endDate.IsZero() { - dateFilter += fmt.Sprintf(" AND dls.summary_date <= '%s'", endDate.Format("2006-01-02")) + conditions = append(conditions, "dls.summary_date <= ?") + args = append(args, endDate.Format("2006-01-02")) } - query = fmt.Sprintf(query, dateFilter) + if len(conditions) > 0 { + query += " WHERE " + strings.Join(conditions, " AND ") + } - if err := db.Raw(query).Scan(&results).Error; err != nil { + query += ` + GROUP BY d.id, d.name, d.code + ORDER BY (COALESCE(SUM(dls.incoming_count), 0) + COALESCE(SUM(dls.outgoing_count), 0)) DESC` + + log.Printf("Summary query: %s, args: %v", query, args) + + if err := db.Raw(query, args...).Scan(&results).Error; err != nil { return nil, err } - // If no results from summary table, fall back to direct query - if len(results) == 0 { - fallbackQuery := ` - SELECT - d.id as department_id, - d.name as department_name, - d.code as department_code, - COUNT(DISTINCT lir.letter_id) as incoming_count, - COUNT(DISTINCT lor.letter_id) as outgoing_count, - COUNT(DISTINCT CASE WHEN lo.status = 'pending_approval' THEN lo.id END) as pending_count, - COALESCE(AVG(CASE - WHEN lo.status IN ('approved', 'sent', 'archived') - THEN EXTRACT(EPOCH FROM (lo.updated_at - lo.created_at))/3600 - END), 0) as avg_response_time, - CASE - WHEN COUNT(DISTINCT lo.id) > 0 - THEN ROUND(COUNT(DISTINCT CASE WHEN lo.status IN ('sent', 'archived') THEN lo.id END) * 100.0 / COUNT(DISTINCT lo.id), 2) - ELSE 0 - END as completion_rate - FROM departments d - LEFT JOIN letter_incoming_recipients lir ON lir.recipient_department_id = d.id - LEFT JOIN letter_outgoing_recipients lor ON lor.department_id = d.id - LEFT JOIN letters_outgoing lo ON lo.id = lor.letter_id AND lo.deleted_at IS NULL - WHERE 1=1 - %s - GROUP BY d.id, d.name, d.code - ORDER BY (COUNT(DISTINCT lir.letter_id) + COUNT(DISTINCT lor.letter_id)) DESC - ` + // Check if summary table has data for this period + var summaryCount int64 + checkQuery := "SELECT COUNT(*) FROM department_letter_summary WHERE 1=1" + checkArgs := []interface{}{} + + if !startDate.IsZero() { + checkQuery += " AND summary_date >= ?" + checkArgs = append(checkArgs, startDate.Format("2006-01-02")) + } + if !endDate.IsZero() { + checkQuery += " AND summary_date <= ?" + checkArgs = append(checkArgs, endDate.Format("2006-01-02")) + } + + db.Raw(checkQuery, checkArgs...).Scan(&summaryCount) + log.Printf("Summary count: %d", summaryCount) + + // If no summary data exists for this period, fall back to direct query + if summaryCount == 0 { + log.Println("Using fallback query (no summary data)") + + // Use CTE for better performance + fallbackQuery := ` + WITH filtered_incoming AS ( + SELECT + li.id, + li.created_at, + li.updated_at, + lir.recipient_department_id + FROM letters_incoming li + INNER JOIN letter_incoming_recipients lir ON lir.letter_id = li.id + WHERE li.deleted_at IS NULL` + + var fallbackArgs []interface{} - fallbackDateFilter := "" if !startDate.IsZero() { - fallbackDateFilter += fmt.Sprintf(" AND (lo.created_at >= '%s' OR lo.created_at IS NULL)", startDate.Format("2006-01-02")) + fallbackQuery += " AND li.created_at >= ?" + fallbackArgs = append(fallbackArgs, startDate) } if !endDate.IsZero() { - fallbackDateFilter += fmt.Sprintf(" AND (lo.created_at <= '%s' OR lo.created_at IS NULL)", endDate.Format("2006-01-02")) + fallbackQuery += " AND li.created_at <= ?" + fallbackArgs = append(fallbackArgs, endDate) } - fallbackQuery = fmt.Sprintf(fallbackQuery, fallbackDateFilter) + fallbackQuery += ` + ), + filtered_outgoing AS ( + SELECT + lo.id, + lo.status, + lo.created_at, + lo.updated_at, + lor.department_id + FROM letters_outgoing lo + INNER JOIN letter_outgoing_recipients lor ON lor.letter_id = lo.id + WHERE lo.deleted_at IS NULL` - if err := db.Raw(fallbackQuery).Scan(&results).Error; err != nil { + if !startDate.IsZero() { + fallbackQuery += " AND lo.created_at >= ?" + fallbackArgs = append(fallbackArgs, startDate) + } + if !endDate.IsZero() { + fallbackQuery += " AND lo.created_at <= ?" + fallbackArgs = append(fallbackArgs, endDate) + } + + fallbackQuery += ` + ) + SELECT + d.id as department_id, + d.name as department_name, + d.code as department_code, + COUNT(DISTINCT fi.id) as incoming_count, + COUNT(DISTINCT fo.id) as outgoing_count, + COUNT(DISTINCT CASE WHEN fo.status = 'pending_approval' THEN fo.id END) as pending_count, + COALESCE(AVG(CASE + WHEN fo.status IN ('approved', 'sent', 'archived') + THEN EXTRACT(EPOCH FROM (fo.updated_at - fo.created_at))/3600 + END), 0) as avg_response_time, + CASE + WHEN COUNT(DISTINCT fo.id) > 0 + THEN ROUND(COUNT(DISTINCT CASE WHEN fo.status IN ('sent', 'archived') THEN fo.id END) * 100.0 / COUNT(DISTINCT fo.id), 2) + ELSE 0 + END as completion_rate + FROM departments d + LEFT JOIN filtered_incoming fi ON fi.recipient_department_id = d.id + LEFT JOIN filtered_outgoing fo ON fo.department_id = d.id + GROUP BY d.id, d.name, d.code + ORDER BY (COUNT(DISTINCT fi.id) + COUNT(DISTINCT fo.id)) DESC` + + log.Printf("Fallback query: %s", fallbackQuery) + log.Printf("Fallback args: %v", fallbackArgs) + + if err := db.Raw(fallbackQuery, fallbackArgs...).Scan(&results).Error; err != nil { return nil, err } }