package controllers import ( "errors" "hr_receiver/config" "hr_receiver/models" "net/http" "strconv" "strings" "github.com/gin-gonic/gin" "gorm.io/gorm" ) type StatisticsController struct { DB *gorm.DB } func NewStatisticsController() *StatisticsController { return &StatisticsController{DB: config.DB} } // --- 请求参数 --- type analysisRecordListParams struct { PageNum int `form:"pageNum,default=1"` PageSize int `form:"pageSize,default=10"` RegionID uint32 `form:"regionId"` StartTime int64 `form:"startTime"` EndTime int64 `form:"endTime"` } // --- 查询接口 --- func (sc *StatisticsController) ListAIAnalysisRecords(c *gin.Context) { var params analysisRecordListParams if err := c.ShouldBindQuery(¶ms); err != nil { writeError(c, http.StatusBadRequest, err.Error()) return } if params.PageNum < 1 { params.PageNum = 1 } if params.PageSize < 1 || params.PageSize > 100 { params.PageSize = 10 } offset := (params.PageNum - 1) * params.PageSize query := sc.DB.Model(&models.AIAnalysisRecord{}) if params.RegionID > 0 { query = query.Where("region_id = ?", params.RegionID) } if params.StartTime > 0 { query = query.Where("upload_time >= ?", params.StartTime) } if params.EndTime > 0 { query = query.Where("upload_time <= ?", params.EndTime) } var total int64 if err := query.Count(&total).Error; err != nil { writeError(c, http.StatusInternalServerError, "failed to count records") return } var records []models.AIAnalysisRecord if err := query.Order("created_at DESC").Offset(offset).Limit(params.PageSize).Find(&records).Error; err != nil { writeError(c, http.StatusInternalServerError, "failed to query records") return } writeSuccess(c, http.StatusOK, "query success", gin.H{ "list": records, "pagination": gin.H{ "currentPage": params.PageNum, "pageSize": params.PageSize, "totalList": total, "totalPage": int((total + int64(params.PageSize) - 1) / int64(params.PageSize)), }, }) } // --- 删除接口 --- func (sc *StatisticsController) DeleteAIAnalysisRecord(c *gin.Context) { id := strings.TrimSpace(c.Param("id")) if id == "" { writeError(c, http.StatusBadRequest, "id is required") return } var record models.AIAnalysisRecord if err := sc.DB.First(&record, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { writeError(c, http.StatusNotFound, "record not found") return } writeError(c, http.StatusInternalServerError, "failed to query record") return } if err := sc.DB.Delete(&record).Error; err != nil { writeError(c, http.StatusInternalServerError, "failed to delete record") return } writeSuccess(c, http.StatusOK, "delete success", nil) } // --- 统计接口 --- type regionStatisticsItem struct { RegionID uint32 `json:"regionId"` Count int64 `json:"count"` TotalInputTokens int64 `json:"totalInputTokens"` TotalOutputTokens int64 `json:"totalOutputTokens"` TotalInputSizeBytes int64 `json:"totalInputSizeBytes"` TotalOutputSizeBytes int64 `json:"totalOutputSizeBytes"` TotalDurationMs int64 `json:"totalDurationMs"` AvgDurationMs float64 `json:"avgDurationMs"` TotalOriginalFileSize int64 `json:"totalOriginalFileSize"` TotalCompressedSize int64 `json:"totalCompressedSize"` } func (sc *StatisticsController) StatisticsByRegion(c *gin.Context) { regionIDStr := c.Query("regionId") startTimeStr := c.Query("startTime") endTimeStr := c.Query("endTime") query := sc.DB.Model(&models.AIAnalysisRecord{}) if regionIDStr != "" { if regionID, err := strconv.ParseUint(regionIDStr, 10, 32); err == nil { query = query.Where("region_id = ?", uint32(regionID)) } } if startTimeStr != "" { if startTime, err := strconv.ParseInt(startTimeStr, 10, 64); err == nil { query = query.Where("upload_time >= ?", startTime) } } if endTimeStr != "" { if endTime, err := strconv.ParseInt(endTimeStr, 10, 64); err == nil { query = query.Where("upload_time <= ?", endTime) } } type rawStats struct { RegionID *uint32 Count int64 TotalInputTokens int64 TotalOutputTokens int64 TotalInputSizeBytes int64 TotalOutputSizeBytes int64 TotalDurationMs int64 TotalOriginalFileSize int64 TotalCompressedSize int64 } var rawResults []rawStats err := query.Select(` region_id, COUNT(*) as count, COALESCE(SUM(input_tokens), 0) as total_input_tokens, COALESCE(SUM(output_tokens), 0) as total_output_tokens, COALESCE(SUM(input_size_bytes), 0) as total_input_size_bytes, COALESCE(SUM(output_size_bytes), 0) as total_output_size_bytes, COALESCE(SUM(duration_ms), 0) as total_duration_ms, COALESCE(SUM(original_file_size), 0) as total_original_file_size, COALESCE(SUM(compressed_content_size), 0) as total_compressed_size `).Group("region_id").Scan(&rawResults).Error if err != nil { writeError(c, http.StatusInternalServerError, "failed to query statistics") return } overall := regionStatisticsItem{} regions := make(map[string]regionStatisticsItem, len(rawResults)) for _, r := range rawResults { regionID := uint32(0) if r.RegionID != nil { regionID = *r.RegionID } avgDuration := float64(0) if r.Count > 0 { avgDuration = float64(r.TotalDurationMs) / float64(r.Count) } item := regionStatisticsItem{ RegionID: regionID, Count: r.Count, TotalInputTokens: r.TotalInputTokens, TotalOutputTokens: r.TotalOutputTokens, TotalInputSizeBytes: r.TotalInputSizeBytes, TotalOutputSizeBytes: r.TotalOutputSizeBytes, TotalDurationMs: r.TotalDurationMs, AvgDurationMs: avgDuration, TotalOriginalFileSize: r.TotalOriginalFileSize, TotalCompressedSize: r.TotalCompressedSize, } regions[strconv.FormatUint(uint64(regionID), 10)] = item overall.Count += r.Count overall.TotalInputTokens += r.TotalInputTokens overall.TotalOutputTokens += r.TotalOutputTokens overall.TotalInputSizeBytes += r.TotalInputSizeBytes overall.TotalOutputSizeBytes += r.TotalOutputSizeBytes overall.TotalDurationMs += r.TotalDurationMs overall.TotalOriginalFileSize += r.TotalOriginalFileSize overall.TotalCompressedSize += r.TotalCompressedSize } if overall.Count > 0 { overall.AvgDurationMs = float64(overall.TotalDurationMs) / float64(overall.Count) } writeSuccess(c, http.StatusOK, "query success", gin.H{ "overall": overall, "regions": regions, }) }