update dashboard analytic
This commit is contained in:
parent
ea980f8cf7
commit
f391b6d853
@ -27,7 +27,7 @@ func (r *AnalyticsRepository) GetLetterSummaryStats(ctx context.Context, startDa
|
||||
// Use department_letter_summary for department-specific stats
|
||||
query := db.Table("department_letter_summary").
|
||||
Where("department_id = ?", *departmentID)
|
||||
|
||||
|
||||
if !startDate.IsZero() {
|
||||
query = query.Where("summary_date >= ?", startDate)
|
||||
}
|
||||
@ -36,13 +36,13 @@ func (r *AnalyticsRepository) GetLetterSummaryStats(ctx context.Context, startDa
|
||||
}
|
||||
|
||||
var result struct {
|
||||
TotalIncoming int64 `gorm:"column:total_incoming"`
|
||||
TotalOutgoing int64 `gorm:"column:total_outgoing"`
|
||||
PendingOutgoing int64 `gorm:"column:pending_outgoing"`
|
||||
ApprovedOutgoing int64 `gorm:"column:approved_outgoing"`
|
||||
RejectedOutgoing int64 `gorm:"column:rejected_outgoing"`
|
||||
AvgResponseHours float64 `gorm:"column:avg_response_hours"`
|
||||
CompletionRate float64 `gorm:"column:completion_rate"`
|
||||
TotalIncoming int64 `gorm:"column:total_incoming"`
|
||||
TotalOutgoing int64 `gorm:"column:total_outgoing"`
|
||||
PendingOutgoing int64 `gorm:"column:pending_outgoing"`
|
||||
ApprovedOutgoing int64 `gorm:"column:approved_outgoing"`
|
||||
RejectedOutgoing int64 `gorm:"column:rejected_outgoing"`
|
||||
AvgResponseHours float64 `gorm:"column:avg_response_hours"`
|
||||
CompletionRate float64 `gorm:"column:completion_rate"`
|
||||
}
|
||||
|
||||
query.Select(`
|
||||
@ -66,7 +66,7 @@ func (r *AnalyticsRepository) GetLetterSummaryStats(ctx context.Context, startDa
|
||||
} else if userID == nil && departmentID == nil {
|
||||
// Use letter_summary for overall stats
|
||||
query := db.Table("letter_summary")
|
||||
|
||||
|
||||
if !startDate.IsZero() {
|
||||
query = query.Where("summary_date >= ?", startDate)
|
||||
}
|
||||
@ -75,14 +75,14 @@ func (r *AnalyticsRepository) GetLetterSummaryStats(ctx context.Context, startDa
|
||||
}
|
||||
|
||||
var result struct {
|
||||
TotalIncoming int64 `gorm:"column:total_incoming"`
|
||||
TotalOutgoing int64 `gorm:"column:total_outgoing"`
|
||||
TotalPending int64 `gorm:"column:total_pending"`
|
||||
TotalApproved int64 `gorm:"column:total_approved"`
|
||||
TotalRejected int64 `gorm:"column:total_rejected"`
|
||||
TotalArchived int64 `gorm:"column:total_archived"`
|
||||
TotalSent int64 `gorm:"column:total_sent"`
|
||||
AvgProcessing float64 `gorm:"column:avg_processing"`
|
||||
TotalIncoming int64 `gorm:"column:total_incoming"`
|
||||
TotalOutgoing int64 `gorm:"column:total_outgoing"`
|
||||
TotalPending int64 `gorm:"column:total_pending"`
|
||||
TotalApproved int64 `gorm:"column:total_approved"`
|
||||
TotalRejected int64 `gorm:"column:total_rejected"`
|
||||
TotalArchived int64 `gorm:"column:total_archived"`
|
||||
TotalSent int64 `gorm:"column:total_sent"`
|
||||
AvgProcessing float64 `gorm:"column:avg_processing"`
|
||||
}
|
||||
|
||||
query.Select(`
|
||||
@ -132,39 +132,44 @@ func (r *AnalyticsRepository) GetLetterSummaryStats(ctx context.Context, startDa
|
||||
outgoingQuery = outgoingQuery.
|
||||
Joins("LEFT JOIN letter_outgoing_recipients ON letter_outgoing_recipients.letter_id = letters_outgoing.id").
|
||||
Where("letter_outgoing_recipients.user_id = ?", *userID)
|
||||
incomingQuery = incomingQuery.
|
||||
Joins("LEFT JOIN letter_incoming_recipients ON letter_incoming_recipients.letter_id = letters_incoming.id").
|
||||
Where("letter_incoming_recipients.recipient_user_id = ?", *userID)
|
||||
}
|
||||
|
||||
fmt.Printf("[DEBUG] userId analitycs: %v\n", userID)
|
||||
|
||||
// Count incoming letters
|
||||
var totalIncoming int64
|
||||
incomingQuery.Count(&totalIncoming)
|
||||
incomingQuery.Distinct("letters_incoming.id").Count(&totalIncoming)
|
||||
stats["total_incoming"] = totalIncoming
|
||||
|
||||
// Count outgoing letters
|
||||
var totalOutgoing int64
|
||||
outgoingQuery.Count(&totalOutgoing)
|
||||
outgoingQuery.Distinct("letters_outgoing.id").Count(&totalOutgoing)
|
||||
stats["total_outgoing"] = totalOutgoing
|
||||
|
||||
// Count by status - need to clone query for each count
|
||||
var pendingCount, approvedCount, rejectedCount, archivedCount int64
|
||||
|
||||
|
||||
db.Table("letters_outgoing").Where("letters_outgoing.deleted_at IS NULL").
|
||||
Where("letters_outgoing.status = ?", "pending_approval").
|
||||
Joins("LEFT JOIN letter_outgoing_recipients ON letter_outgoing_recipients.letter_id = letters_outgoing.id").
|
||||
Where("letter_outgoing_recipients.user_id = ?", *userID).
|
||||
Count(&pendingCount)
|
||||
|
||||
|
||||
db.Table("letters_outgoing").Where("letters_outgoing.deleted_at IS NULL").
|
||||
Where("letters_outgoing.status = ?", "approved").
|
||||
Joins("LEFT JOIN letter_outgoing_recipients ON letter_outgoing_recipients.letter_id = letters_outgoing.id").
|
||||
Where("letter_outgoing_recipients.user_id = ?", *userID).
|
||||
Count(&approvedCount)
|
||||
|
||||
|
||||
db.Table("letters_outgoing").Where("letters_outgoing.deleted_at IS NULL").
|
||||
Where("letters_outgoing.status = ?", "rejected").
|
||||
Joins("LEFT JOIN letter_outgoing_recipients ON letter_outgoing_recipients.letter_id = letters_outgoing.id").
|
||||
Where("letter_outgoing_recipients.user_id = ?", *userID).
|
||||
Count(&rejectedCount)
|
||||
|
||||
|
||||
db.Table("letters_outgoing").Where("letters_outgoing.deleted_at IS NULL").
|
||||
Where("letters_outgoing.status = ?", "archived").
|
||||
Joins("LEFT JOIN letter_outgoing_recipients ON letter_outgoing_recipients.letter_id = letters_outgoing.id").
|
||||
@ -386,7 +391,7 @@ func (r *AnalyticsRepository) GetDepartmentStats(ctx context.Context, startDate,
|
||||
}
|
||||
|
||||
fallbackQuery = fmt.Sprintf(fallbackQuery, fallbackDateFilter)
|
||||
|
||||
|
||||
if err := db.Raw(fallbackQuery).Scan(&results).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -471,9 +476,9 @@ func (r *AnalyticsRepository) GetMonthlyTrend(ctx context.Context, months int) (
|
||||
ORDER BY year DESC, month_num DESC
|
||||
LIMIT %d
|
||||
`
|
||||
|
||||
|
||||
fallbackQuery = fmt.Sprintf(fallbackQuery, months, months, months)
|
||||
|
||||
|
||||
if err := db.Raw(fallbackQuery).Scan(&results).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -713,6 +718,66 @@ func (r *AnalyticsRepository) GetDailyActivity(ctx context.Context, days int) ([
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (r *AnalyticsRepository) GetDailyActivityByUserID(ctx context.Context, userID *uuid.UUID, days int) ([]map[string]interface{}, error) {
|
||||
db := DBFromContext(ctx, r.db)
|
||||
var results []map[string]interface{}
|
||||
|
||||
query := `
|
||||
WITH daily_data AS (
|
||||
SELECT
|
||||
DATE(created_at) as date,
|
||||
TO_CHAR(created_at, 'Day') as day_of_week,
|
||||
COUNT(CASE WHEN type = 'incoming' THEN 1 END) as incoming_count,
|
||||
COUNT(CASE WHEN type = 'outgoing' THEN 1 END) as outgoing_count,
|
||||
0 as approved_count,
|
||||
0 as rejected_count
|
||||
FROM (
|
||||
SELECT li.created_at, 'incoming' as type
|
||||
FROM letters_incoming li
|
||||
INNER JOIN letter_incoming_recipients lir ON lir.letter_id = li.id
|
||||
WHERE li.deleted_at IS NULL AND lir.recipient_user_id = ?
|
||||
UNION ALL
|
||||
SELECT lo.created_at, 'outgoing' as type
|
||||
FROM letters_outgoing lo
|
||||
INNER JOIN letter_outgoing_recipients lor ON lor.letter_id = lo.id
|
||||
WHERE lo.deleted_at IS NULL AND lor.user_id = ?
|
||||
) combined
|
||||
WHERE created_at >= CURRENT_DATE - INTERVAL '%d days'
|
||||
GROUP BY DATE(created_at), TO_CHAR(created_at, 'Day')
|
||||
),
|
||||
approval_data AS (
|
||||
SELECT
|
||||
DATE(acted_at) as date,
|
||||
COUNT(CASE WHEN status = 'approved' THEN 1 END) as approved_count,
|
||||
COUNT(CASE WHEN status = 'rejected' THEN 1 END) as rejected_count
|
||||
FROM letter_outgoing_approvals
|
||||
WHERE acted_at IS NOT NULL
|
||||
AND acted_at >= CURRENT_DATE - INTERVAL '%d days'
|
||||
AND approver_id = ?
|
||||
GROUP BY DATE(acted_at)
|
||||
)
|
||||
SELECT
|
||||
d.date,
|
||||
d.day_of_week,
|
||||
d.incoming_count,
|
||||
d.outgoing_count,
|
||||
COALESCE(a.approved_count, 0) as approved_count,
|
||||
COALESCE(a.rejected_count, 0) as rejected_count
|
||||
FROM daily_data d
|
||||
LEFT JOIN approval_data a ON a.date = d.date
|
||||
ORDER BY d.date DESC
|
||||
LIMIT %d
|
||||
`
|
||||
|
||||
query = fmt.Sprintf(query, days, days, days)
|
||||
|
||||
if err := db.Raw(query, userID, userID, userID).Scan(&results).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetResponseTimeStats gets response time statistics
|
||||
func (r *AnalyticsRepository) GetResponseTimeStats(ctx context.Context, startDate, endDate time.Time) (map[string]interface{}, error) {
|
||||
db := DBFromContext(ctx, r.db)
|
||||
|
||||
@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"eslogad-be/internal/appcontext"
|
||||
@ -37,7 +38,7 @@ func (s *AnalyticsServiceImpl) GetDashboard(ctx context.Context, req *contract.A
|
||||
// Default to last 30 days
|
||||
startDate = time.Now().AddDate(0, 0, -30)
|
||||
}
|
||||
|
||||
|
||||
if req.EndDate != "" {
|
||||
if date, err := time.Parse("2006-01-02", req.EndDate); err == nil {
|
||||
endDate = date.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
|
||||
@ -48,7 +49,7 @@ func (s *AnalyticsServiceImpl) GetDashboard(ctx context.Context, req *contract.A
|
||||
|
||||
// Apply user context filters if not admin
|
||||
var userID *uuid.UUID
|
||||
|
||||
|
||||
appCtx := appcontext.FromGinContext(ctx)
|
||||
if appCtx != nil && appCtx.UserRole != "admin" && appCtx.UserRole != "superadmin" {
|
||||
userID = &appCtx.UserID
|
||||
@ -61,8 +62,9 @@ func (s *AnalyticsServiceImpl) GetDashboard(ctx context.Context, req *contract.A
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("[DEBUG] summaryData: %v\n", summaryData)
|
||||
response.Summary = s.mapSummaryStats(summaryData)
|
||||
|
||||
|
||||
// Calculate growth metrics
|
||||
response.Summary.WeekOverWeekGrowth = s.calculateWeekOverWeekGrowth(ctx)
|
||||
response.Summary.MonthOverMonthGrowth = s.calculateMonthOverMonthGrowth(ctx)
|
||||
@ -99,7 +101,7 @@ func (s *AnalyticsServiceImpl) GetDashboard(ctx context.Context, req *contract.A
|
||||
response.InstitutionStats = s.mapInstitutionStats(instData)
|
||||
|
||||
// Get daily activity (last 7 days)
|
||||
dailyData, err := s.analyticsRepo.GetDailyActivity(ctx, 7)
|
||||
dailyData, err := s.analyticsRepo.GetDailyActivityByUserID(ctx, userID, 7)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -143,17 +145,17 @@ func (s *AnalyticsServiceImpl) calculateWeekOverWeekGrowth(ctx context.Context)
|
||||
// Get this week's data
|
||||
thisWeekStart := time.Now().AddDate(0, 0, -int(time.Now().Weekday()))
|
||||
thisWeekEnd := time.Now()
|
||||
|
||||
|
||||
// Get last week's data
|
||||
lastWeekStart := thisWeekStart.AddDate(0, 0, -7)
|
||||
lastWeekEnd := thisWeekStart.AddDate(0, 0, -1)
|
||||
|
||||
|
||||
thisWeekData, _ := s.analyticsRepo.GetLetterSummaryStats(ctx, thisWeekStart, thisWeekEnd, nil, nil)
|
||||
lastWeekData, _ := s.analyticsRepo.GetLetterSummaryStats(ctx, lastWeekStart, lastWeekEnd, nil, nil)
|
||||
|
||||
|
||||
thisWeekTotal := getInt64Value(thisWeekData["total_incoming"]) + getInt64Value(thisWeekData["total_outgoing"])
|
||||
lastWeekTotal := getInt64Value(lastWeekData["total_incoming"]) + getInt64Value(lastWeekData["total_outgoing"])
|
||||
|
||||
|
||||
if lastWeekTotal > 0 {
|
||||
return float64((thisWeekTotal - lastWeekTotal) * 100 / lastWeekTotal)
|
||||
}
|
||||
@ -166,17 +168,17 @@ func (s *AnalyticsServiceImpl) calculateMonthOverMonthGrowth(ctx context.Context
|
||||
now := time.Now()
|
||||
thisMonthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
|
||||
thisMonthEnd := now
|
||||
|
||||
|
||||
// Get last month's data
|
||||
lastMonthStart := thisMonthStart.AddDate(0, -1, 0)
|
||||
lastMonthEnd := thisMonthStart.AddDate(0, 0, -1)
|
||||
|
||||
|
||||
thisMonthData, _ := s.analyticsRepo.GetLetterSummaryStats(ctx, thisMonthStart, thisMonthEnd, nil, nil)
|
||||
lastMonthData, _ := s.analyticsRepo.GetLetterSummaryStats(ctx, lastMonthStart, lastMonthEnd, nil, nil)
|
||||
|
||||
|
||||
thisMonthTotal := getInt64Value(thisMonthData["total_incoming"]) + getInt64Value(thisMonthData["total_outgoing"])
|
||||
lastMonthTotal := getInt64Value(lastMonthData["total_incoming"]) + getInt64Value(lastMonthData["total_outgoing"])
|
||||
|
||||
|
||||
if lastMonthTotal > 0 {
|
||||
return float64((thisMonthTotal - lastMonthTotal) * 100 / lastMonthTotal)
|
||||
}
|
||||
@ -190,7 +192,7 @@ func (s *AnalyticsServiceImpl) getSimpleDepartmentStats(ctx context.Context, sta
|
||||
if err != nil {
|
||||
return []contract.SimpleDepartmentStats{}
|
||||
}
|
||||
|
||||
|
||||
result := make([]contract.SimpleDepartmentStats, 0, len(deptData))
|
||||
for _, item := range deptData {
|
||||
deptIDStr := getStringValue(item["department_id"])
|
||||
@ -198,17 +200,17 @@ func (s *AnalyticsServiceImpl) getSimpleDepartmentStats(ctx context.Context, sta
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
// Calculate total letter count (incoming + outgoing)
|
||||
letterCount := getInt64Value(item["incoming_count"]) + getInt64Value(item["outgoing_count"])
|
||||
|
||||
|
||||
result = append(result, contract.SimpleDepartmentStats{
|
||||
DepartmentID: deptID,
|
||||
Department: getStringValue(item["department_name"]),
|
||||
LetterCount: letterCount,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@ -303,11 +305,11 @@ func (s *AnalyticsServiceImpl) mapInstitutionStats(data []map[string]interface{}
|
||||
OutgoingCount: getInt64Value(item["outgoing_count"]),
|
||||
TotalCount: getInt64Value(item["total_count"]),
|
||||
}
|
||||
|
||||
|
||||
if lastActivity, ok := item["last_activity"].(time.Time); ok {
|
||||
stat.LastActivity = lastActivity
|
||||
}
|
||||
|
||||
|
||||
result = append(result, stat)
|
||||
}
|
||||
}
|
||||
@ -410,4 +412,4 @@ func getFloat64Value(v interface{}) float64 {
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user