feat(reports): add analytics timeseries rollups

This commit is contained in:
2026-06-05 21:14:45 +08:00
parent a48b9cda8e
commit 43aa2f81eb
11 changed files with 494 additions and 32 deletions
@@ -37,6 +37,7 @@ func (s *AnalyticsHandlerTestSuite) SetupSuite() {
s.db = db
s.Require().NoError(db.AutoMigrate(
&model.Account{},
&model.Conversation{},
&model.ReportingEvent{},
&model.ReportingEventsRollup{},
))
@@ -54,6 +55,7 @@ func (s *AnalyticsHandlerTestSuite) SetupSuite() {
gin.SetMode(gin.TestMode)
r := gin.New()
accounts := r.Group("/api/v1/accounts/:account_id")
accounts.GET("/reports", s.handler.Index)
accounts.GET("/reports/summary", s.handler.Summary)
accounts.GET("/reports/agents", s.handler.AgentMetrics)
accounts.GET("/reports/inboxes", s.handler.InboxMetrics)
@@ -70,6 +72,8 @@ func (s *AnalyticsHandlerTestSuite) TearDownSuite() {
func (s *AnalyticsHandlerTestSuite) SetupTest() {
s.db.Exec("DELETE FROM reporting_events")
s.db.Exec("DELETE FROM reporting_events_rollups")
s.db.Exec("DELETE FROM conversations")
}
// ========== parseAccountID / parseDateRange edge cases ==========
@@ -149,6 +153,23 @@ func (s *AnalyticsHandlerTestSuite) TestSummary_WithData() {
s.Equal(http.StatusOK, w.Code)
}
func (s *AnalyticsHandlerTestSuite) TestIndex_TimeseriesWithData() {
conv := model.Conversation{AccountID: s.accountID, InboxID: 1, ContactID: 1, Status: string(model.ConversationStatusOpen), ChannelType: "web_widget", Channel: "web_widget"}
s.Require().NoError(s.db.Create(&conv).Error)
s.Require().NoError(s.db.Model(&conv).Updates(map[string]interface{}{"created_at": parseTime("2025-01-15T10:00:00Z")}).Error)
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet,
"/api/v1/accounts/1/reports?metric=conversations_count&since=2025-01-01T00:00:00Z&until=2025-02-01T00:00:00Z&type=account&group_by=day", nil)
s.router.ServeHTTP(w, req)
s.Equal(http.StatusOK, w.Code)
var body map[string]interface{}
s.NoError(json.Unmarshal(w.Body.Bytes(), &body))
data := body["data"].([]interface{})
s.Len(data, 1)
s.Equal(float64(1), data[0].(map[string]interface{})["value"])
}
// ========== AgentMetrics ==========
func (s *AnalyticsHandlerTestSuite) TestAgentMetrics_InvalidAccountID() {
@@ -251,4 +272,4 @@ func (s *AnalyticsHandlerTestSuite) TestNilService() {
func parseTime(s string) time.Time {
t, _ := time.Parse(time.RFC3339, s)
return t
}
}