turash/bugulma/backend/internal/service/admin_service.go

260 lines
7.7 KiB
Go

package service
import (
"context"
"fmt"
"time"
"bugulma/backend/internal/domain"
)
type AdminService struct {
orgService *OrganizationService
userService *UserService
verificationService *VerificationService
i18nService *I18nService
}
func NewAdminService(
orgService *OrganizationService,
userService *UserService,
verificationService *VerificationService,
i18nService *I18nService,
) *AdminService {
return &AdminService{
orgService: orgService,
userService: userService,
verificationService: verificationService,
i18nService: i18nService,
}
}
// DashboardStats represents dashboard statistics
type DashboardStats struct {
TotalOrganizations int64 `json:"totalOrganizations"`
VerifiedOrganizations int64 `json:"verifiedOrganizations"`
ActiveConnections int64 `json:"activeConnections"`
NewThisMonth int64 `json:"newThisMonth"`
PendingVerifications int64 `json:"pendingVerifications"`
PendingTranslations int64 `json:"pendingTranslations"`
SystemAlerts int64 `json:"systemAlerts"`
}
// GetDashboardStats retrieves dashboard statistics
func (s *AdminService) GetDashboardStats(ctx context.Context) (*DashboardStats, error) {
// Get all organizations
allOrgs, err := s.orgService.GetAll(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get organizations: %w", err)
}
stats := &DashboardStats{
TotalOrganizations: int64(len(allOrgs)),
}
// Count verified organizations
verifiedCount := int64(0)
now := time.Now()
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
newThisMonth := int64(0)
for _, org := range allOrgs {
if org.Verified {
verifiedCount++
}
if org.CreatedAt.After(monthStart) {
newThisMonth++
}
}
stats.VerifiedOrganizations = verifiedCount
stats.NewThisMonth = newThisMonth
// Get pending verifications
verificationQueue, err := s.verificationService.GetVerificationQueue(ctx, VerificationQueueFilters{
Status: func() *domain.VerificationStatus {
status := domain.VerificationPending
return &status
}(),
})
if err == nil {
stats.PendingVerifications = int64(len(verificationQueue))
}
// Get pending translations (simplified - would need more detailed i18n stats)
translationStats, err := s.i18nService.GetTranslationStats(ctx, "")
if err == nil {
// Calculate missing translations
if totalKeys, ok := translationStats["total_unique_keys"].(int64); ok {
if translatedCounts, ok := translationStats["translated_counts"].(map[string]int64); ok {
// Count missing for each locale
var totalMissing int64
for locale, count := range translatedCounts {
if locale != DefaultLocale {
missing := totalKeys - count
if missing > 0 {
totalMissing += missing
}
}
}
stats.PendingTranslations = totalMissing
}
}
}
// Active connections would come from matching/proposal service
// For now, set to 0
stats.ActiveConnections = 0
stats.SystemAlerts = 0
return stats, nil
}
// OrganizationStats represents organization statistics
type OrganizationStats struct {
Total int64 `json:"total"`
Verified int64 `json:"verified"`
Pending int64 `json:"pending"`
Unverified int64 `json:"unverified"`
NewThisMonth int64 `json:"newThisMonth"`
BySector map[string]int64 `json:"bySector"`
BySubtype map[string]int64 `json:"bySubtype"`
VerificationRate float64 `json:"verificationRate"`
}
// GetOrganizationStats retrieves organization statistics
func (s *AdminService) GetOrganizationStats(ctx context.Context, filters map[string]interface{}) (*OrganizationStats, error) {
allOrgs, err := s.orgService.GetAll(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get organizations: %w", err)
}
stats := &OrganizationStats{
Total: int64(len(allOrgs)),
BySector: make(map[string]int64),
BySubtype: make(map[string]int64),
}
now := time.Now()
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
for _, org := range allOrgs {
if org.Verified {
stats.Verified++
} else {
stats.Unverified++
}
if org.CreatedAt.After(monthStart) {
stats.NewThisMonth++
}
stats.BySector[string(org.Sector)]++
stats.BySubtype[string(org.Subtype)]++
}
// Get pending verifications
verificationQueue, err := s.verificationService.GetVerificationQueue(ctx, VerificationQueueFilters{})
if err == nil {
stats.Pending = int64(len(verificationQueue))
}
if stats.Total > 0 {
stats.VerificationRate = float64(stats.Verified) / float64(stats.Total) * 100
}
return stats, nil
}
// UserActivityStats represents user activity statistics
type UserActivityStats struct {
TotalUsers int64 `json:"totalUsers"`
ActiveUsers int64 `json:"activeUsers"`
InactiveUsers int64 `json:"inactiveUsers"`
NewThisMonth int64 `json:"newThisMonth"`
ByRole map[string]int64 `json:"byRole"`
LastLoginStats map[string]int64 `json:"lastLoginStats"` // last_7_days, last_30_days, never
}
// GetUserActivityStats retrieves user activity statistics
func (s *AdminService) GetUserActivityStats(ctx context.Context, filters map[string]interface{}) (*UserActivityStats, error) {
userStats, err := s.userService.GetUserStats(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get user stats: %w", err)
}
stats := &UserActivityStats{
ByRole: make(map[string]int64),
LastLoginStats: make(map[string]int64),
}
if total, ok := userStats["total"].(int64); ok {
stats.TotalUsers = total
}
if active, ok := userStats["active"].(int); ok {
stats.ActiveUsers = int64(active)
}
if inactive, ok := userStats["inactive"].(int); ok {
stats.InactiveUsers = int64(inactive)
}
if newThisMonth, ok := userStats["new_this_month"].(int); ok {
stats.NewThisMonth = int64(newThisMonth)
}
if byRole, ok := userStats["by_role"].(map[string]int64); ok {
stats.ByRole = byRole
}
return stats, nil
}
// MatchingStats represents matching statistics
type MatchingStats struct {
TotalMatches int64 `json:"totalMatches"`
ActiveMatches int64 `json:"activeMatches"`
SuccessfulMatches int64 `json:"successfulMatches"`
MatchRate float64 `json:"matchRate"`
}
// GetMatchingStats retrieves matching statistics
func (s *AdminService) GetMatchingStats(ctx context.Context, filters map[string]interface{}) (*MatchingStats, error) {
// TODO: Implement when matching service is available
stats := &MatchingStats{
TotalMatches: 0,
ActiveMatches: 0,
SuccessfulMatches: 0,
MatchRate: 0,
}
return stats, nil
}
// SystemHealth represents system health metrics
type SystemHealth struct {
Status string `json:"status"` // healthy, degraded, down
Database string `json:"database"`
Cache string `json:"cache"`
ExternalServices map[string]string `json:"externalServices"`
Uptime int64 `json:"uptime"` // seconds
ResponseTime int64 `json:"responseTime"` // milliseconds
ErrorRate float64 `json:"errorRate"`
ActiveConnections int64 `json:"activeConnections"`
}
// GetSystemHealth retrieves system health metrics
func (s *AdminService) GetSystemHealth(ctx context.Context) (*SystemHealth, error) {
health := &SystemHealth{
Status: "healthy",
Database: "connected",
Cache: "connected",
ExternalServices: make(map[string]string),
Uptime: 0,
ResponseTime: 0,
ErrorRate: 0,
}
// TODO: Implement actual health checks
// - Database connectivity
// - Cache connectivity
// - External service status
// - System metrics
return health, nil
}