feat(ops): add usage_logs partition status to ops dashboard

Add partition management integration to the smart ops system:
- Backend: Add GetUsageLogsPartitionStatus endpoint in OpsHandler
- Backend: Add partition query methods in OpsRepository
- Backend: Add UsageLogsPartitionStatus type in OpsService
- Frontend: Add OpsPartitionStatusCard component
- Frontend: Add partition status display in OpsDashboard
- i18n: Add Chinese and English translations

The partition status card shows:
- Whether usage_logs is partitioned
- Current row count vs threshold (100K)
- Partition count (if partitioned)
- Warning message when partitioning is recommended

This allows administrators to monitor partition status directly
from the ops dashboard without checking server logs.
This commit is contained in:
User
2026-04-16 23:16:17 +08:00
parent eb5adbbae5
commit 60d15d2ba4
10 changed files with 409 additions and 1 deletions

View File

@@ -63,6 +63,11 @@ type OpsRepository interface {
UpsertDailyMetrics(ctx context.Context, startTime, endTime time.Time) error
GetLatestHourlyBucketStart(ctx context.Context) (time.Time, bool, error)
GetLatestDailyBucketDate(ctx context.Context) (time.Time, bool, error)
// Usage logs partition management
IsUsageLogsPartitioned(ctx context.Context) (bool, error)
GetUsageLogsRowCount(ctx context.Context) (int64, error)
GetUsageLogsPartitionCount(ctx context.Context) (int, error)
}
type OpsInsertErrorLogInput struct {

View File

@@ -724,3 +724,72 @@ func sanitizeErrorBodyForStorage(raw string, maxBytes int) (sanitized string, tr
}
return raw, false
}
// ==================== Usage Logs Partition Management ====================
// UsageLogsPartitionStatus represents the partition status of usage_logs table.
type UsageLogsPartitionStatus struct {
IsPartitioned bool `json:"is_partitioned"`
RowCount int64 `json:"row_count"`
PartitionCount int `json:"partition_count"`
ThresholdRows int64 `json:"threshold_rows"` // 100000
NeedsPartitioning bool `json:"needs_partitioning"` // rowCount >= threshold && !isPartitioned
WarningLevel string `json:"warning_level"` // "none", "info", "warning"
LastCheckedAt string `json:"last_checked_at"`
}
// GetUsageLogsPartitionStatus returns the current partition status of usage_logs table.
func (s *OpsService) GetUsageLogsPartitionStatus(ctx context.Context) (*UsageLogsPartitionStatus, error) {
if s.opsRepo == nil {
return nil, errors.New("ops repository not available")
}
status := &UsageLogsPartitionStatus{
ThresholdRows: 100000,
LastCheckedAt: time.Now().UTC().Format(time.RFC3339),
}
// Check if usage_logs is partitioned
isPartitioned, err := s.opsRepo.IsUsageLogsPartitioned(ctx)
if err != nil {
log.Printf("[Ops] GetUsageLogsPartitionStatus check partitioned failed: %v", err)
return nil, err
}
status.IsPartitioned = isPartitioned
// Get row count
rowCount, err := s.opsRepo.GetUsageLogsRowCount(ctx)
if err != nil {
log.Printf("[Ops] GetUsageLogsPartitionStatus get row count failed: %v", err)
return nil, err
}
status.RowCount = rowCount
// Determine partition count if partitioned
if isPartitioned {
count, err := s.opsRepo.GetUsageLogsPartitionCount(ctx)
if err != nil {
log.Printf("[Ops] GetUsageLogsPartitionStatus get partition count failed: %v", err)
// Non-critical, continue
} else {
status.PartitionCount = count
}
}
// Determine warning level and needs_partitioning
if isPartitioned {
status.WarningLevel = "none"
status.NeedsPartitioning = false
} else if rowCount >= status.ThresholdRows {
status.WarningLevel = "warning"
status.NeedsPartitioning = true
} else if rowCount >= 50000 {
status.WarningLevel = "info"
status.NeedsPartitioning = false
} else {
status.WarningLevel = "none"
status.NeedsPartitioning = false
}
return status, nil
}