Files
ragflow/internal/service/stats.go
Haruko386 d766e49128 feat[Go]: implement /system/stats and refactor /system/config/log (#15407)
### What problem does this PR solve?

As title

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
- [x] Refactoring
2026-05-29 19:32:21 +08:00

74 lines
2.5 KiB
Go

//
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package service
import (
"errors"
"ragflow/internal/dao"
)
// ErrTenantNotFound indicates the current user has no tenant relation.
var ErrTenantNotFound = errors.New("Tenant not found!")
// StatPoint matches the frontend [date, value] tuple shape.
type StatPoint [2]interface{}
// StatsResponse matches Python GET /api/v1/system/stats response data.
type StatsResponse struct {
PV []StatPoint `json:"pv"`
UV []StatPoint `json:"uv"`
Speed []StatPoint `json:"speed"`
Tokens []StatPoint `json:"tokens"`
Round []StatPoint `json:"round"`
ThumbUp []StatPoint `json:"thumb_up"`
}
// GetStats returns daily API conversation statistics for the first tenant of a user.
func (s *SystemService) GetStats(userID, fromDate, toDate string, source *string) (*StatsResponse, error) {
userTenantDAO := dao.NewUserTenantDAO()
tenants, err := userTenantDAO.GetByUserID(userID)
if err != nil || len(tenants) == 0 {
return nil, ErrTenantNotFound
}
rows, err := dao.NewAPI4ConversationDAO().Stats(tenants[0].TenantID, fromDate, toDate, source)
if err != nil {
return nil, err
}
response := &StatsResponse{
PV: make([]StatPoint, 0, len(rows)),
UV: make([]StatPoint, 0, len(rows)),
Speed: make([]StatPoint, 0, len(rows)),
Tokens: make([]StatPoint, 0, len(rows)),
Round: make([]StatPoint, 0, len(rows)),
ThumbUp: make([]StatPoint, 0, len(rows)),
}
for _, row := range rows {
response.PV = append(response.PV, StatPoint{row.Dt, row.PV})
response.UV = append(response.UV, StatPoint{row.Dt, row.UV})
response.Speed = append(response.Speed, StatPoint{row.Dt, row.Tokens / (row.Duration + 0.1)})
response.Tokens = append(response.Tokens, StatPoint{row.Dt, row.Tokens / 1000.0})
response.Round = append(response.Round, StatPoint{row.Dt, row.Round})
response.ThumbUp = append(response.ThumbUp, StatPoint{row.Dt, row.ThumbUp})
}
return response, nil
}