usage_statistics.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. package model
  2. import (
  3. "errors"
  4. "one-api/common"
  5. "time"
  6. "gorm.io/gorm"
  7. )
  8. type UsageStatistics struct {
  9. Id int `json:"id" gorm:"primaryKey"`
  10. Date string `json:"date" gorm:"type:varchar(10);not null;index:idx_date;uniqueIndex:uk_date_token_model,composite:date"`
  11. TokenId int `json:"token_id" gorm:"not null;index:idx_token_id;uniqueIndex:uk_date_token_model,composite:token_id"`
  12. TokenName string `json:"token_name" gorm:"type:varchar(255);not null;default:''"`
  13. ModelName string `json:"model_name" gorm:"type:varchar(255);not null;index:idx_model_name;uniqueIndex:uk_date_token_model,composite:model_name"`
  14. TotalRequests int `json:"total_requests" gorm:"not null;default:0"`
  15. SuccessfulRequests int `json:"successful_requests" gorm:"not null;default:0"`
  16. FailedRequests int `json:"failed_requests" gorm:"not null;default:0"`
  17. TotalTokens int `json:"total_tokens" gorm:"not null;default:0"`
  18. PromptTokens int `json:"prompt_tokens" gorm:"not null;default:0"`
  19. CompletionTokens int `json:"completion_tokens" gorm:"not null;default:0"`
  20. TotalQuota int `json:"total_quota" gorm:"not null;default:0"`
  21. CreatedTime int64 `json:"created_time" gorm:"bigint;not null"`
  22. UpdatedTime int64 `json:"updated_time" gorm:"bigint;not null"`
  23. }
  24. func (UsageStatistics) TableName() string {
  25. return "usage_statistics"
  26. }
  27. // GetUsageStatistics 获取用量统计数据
  28. func GetUsageStatistics(startDate, endDate string, tokenId int, modelName string, page, pageSize int) ([]*UsageStatistics, int64, error) {
  29. var statistics []*UsageStatistics
  30. var total int64
  31. query := DB.Model(&UsageStatistics{})
  32. // 添加查询条件
  33. if startDate != "" {
  34. query = query.Where("date >= ?", startDate)
  35. }
  36. if endDate != "" {
  37. query = query.Where("date <= ?", endDate)
  38. }
  39. if tokenId > 0 {
  40. query = query.Where("token_id = ?", tokenId)
  41. }
  42. if modelName != "" {
  43. query = query.Where("model_name LIKE ?", "%"+modelName+"%")
  44. }
  45. // 获取总数
  46. err := query.Count(&total).Error
  47. if err != nil {
  48. return nil, 0, err
  49. }
  50. // 分页查询
  51. offset := (page - 1) * pageSize
  52. err = query.Order("date DESC, token_id ASC, model_name ASC").
  53. Offset(offset).Limit(pageSize).Find(&statistics).Error
  54. return statistics, total, err
  55. }
  56. // GetMonthlyUsageStatistics 获取月度用量统计数据
  57. func GetMonthlyUsageStatistics(startDate, endDate string, tokenId int, modelName string, page, pageSize int) ([]*UsageStatistics, int64, error) {
  58. var statistics []*UsageStatistics
  59. var total int64
  60. // 使用原生SQL查询实现按月分组统计
  61. db := DB.Model(&UsageStatistics{})
  62. // 构建查询条件
  63. conditions := ""
  64. params := []interface{}{}
  65. // 添加日期范围条件(按月查询)
  66. if startDate != "" {
  67. conditions += " AND date >= ?"
  68. params = append(params, startDate+"-01")
  69. }
  70. if endDate != "" {
  71. // 获取 endDate 所在月份的最后一天
  72. if len(endDate) >= 7 {
  73. year := endDate[0:4]
  74. month := endDate[5:7]
  75. conditions += " AND date <= ?"
  76. params = append(params, year+"-"+month+"-31")
  77. }
  78. }
  79. if tokenId > 0 {
  80. conditions += " AND token_id = ?"
  81. params = append(params, tokenId)
  82. }
  83. if modelName != "" {
  84. conditions += " AND model_name LIKE ?"
  85. params = append(params, "%"+modelName+"%")
  86. }
  87. // 构建完整的SQL查询
  88. sql := `
  89. SELECT
  90. MAX(id) as id,
  91. SUBSTR(date, 1, 7) as date,
  92. token_id,
  93. token_name,
  94. model_name,
  95. SUM(total_requests) as total_requests,
  96. SUM(successful_requests) as successful_requests,
  97. SUM(failed_requests) as failed_requests,
  98. SUM(total_tokens) as total_tokens,
  99. SUM(prompt_tokens) as prompt_tokens,
  100. SUM(completion_tokens) as completion_tokens,
  101. SUM(total_quota) as total_quota,
  102. MAX(created_time) as created_time,
  103. MAX(updated_time) as updated_time
  104. FROM usage_statistics
  105. WHERE 1=1` + conditions + `
  106. GROUP BY SUBSTR(date, 1, 7), token_id, token_name, model_name
  107. ORDER BY date DESC, token_id ASC, model_name ASC
  108. `
  109. // 获取总数
  110. countSQL := `
  111. SELECT COUNT(*) as count FROM (
  112. SELECT 1
  113. FROM usage_statistics
  114. WHERE 1=1` + conditions + `
  115. GROUP BY SUBSTR(date, 1, 7), token_id, token_name, model_name
  116. ) as grouped_data
  117. `
  118. var countResult struct {
  119. Count int64 `json:"count"`
  120. }
  121. err := db.Raw(countSQL, params...).Scan(&countResult).Error
  122. if err != nil {
  123. return nil, 0, err
  124. }
  125. total = countResult.Count
  126. // 分页查询
  127. offset := (page - 1) * pageSize
  128. limitSQL := sql + " LIMIT ? OFFSET ?"
  129. params = append(params, pageSize, offset)
  130. err = db.Raw(limitSQL, params...).Scan(&statistics).Error
  131. return statistics, total, err
  132. }
  133. // UpsertUsageStatistics 插入或更新用量统计数据
  134. func UpsertUsageStatistics(date string, tokenId int, tokenName, modelName string,
  135. totalRequests, successfulRequests, failedRequests int,
  136. totalTokens, promptTokens, completionTokens, totalQuota int) error {
  137. now := common.GetTimestamp()
  138. // 使用 ON DUPLICATE KEY UPDATE (MySQL) 或 ON CONFLICT (PostgreSQL)
  139. if common.UsingMySQL {
  140. return DB.Exec(`
  141. INSERT INTO usage_statistics
  142. (date, token_id, token_name, model_name, total_requests, successful_requests, failed_requests,
  143. total_tokens, prompt_tokens, completion_tokens, total_quota, created_time, updated_time)
  144. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  145. ON DUPLICATE KEY UPDATE
  146. token_name = VALUES(token_name),
  147. total_requests = total_requests + VALUES(total_requests),
  148. successful_requests = successful_requests + VALUES(successful_requests),
  149. failed_requests = failed_requests + VALUES(failed_requests),
  150. total_tokens = total_tokens + VALUES(total_tokens),
  151. prompt_tokens = prompt_tokens + VALUES(prompt_tokens),
  152. completion_tokens = completion_tokens + VALUES(completion_tokens),
  153. total_quota = total_quota + VALUES(total_quota),
  154. updated_time = VALUES(updated_time)
  155. `, date, tokenId, tokenName, modelName, totalRequests, successfulRequests, failedRequests,
  156. totalTokens, promptTokens, completionTokens, totalQuota, now, now).Error
  157. } else if common.UsingPostgreSQL {
  158. return DB.Exec(`
  159. INSERT INTO usage_statistics
  160. (date, token_id, token_name, model_name, total_requests, successful_requests, failed_requests,
  161. total_tokens, prompt_tokens, completion_tokens, total_quota, created_time, updated_time)
  162. VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
  163. ON CONFLICT (date, token_id, model_name) DO UPDATE SET
  164. token_name = EXCLUDED.token_name,
  165. total_requests = usage_statistics.total_requests + EXCLUDED.total_requests,
  166. successful_requests = usage_statistics.successful_requests + EXCLUDED.successful_requests,
  167. failed_requests = usage_statistics.failed_requests + EXCLUDED.failed_requests,
  168. total_tokens = usage_statistics.total_tokens + EXCLUDED.total_tokens,
  169. prompt_tokens = usage_statistics.prompt_tokens + EXCLUDED.prompt_tokens,
  170. completion_tokens = usage_statistics.completion_tokens + EXCLUDED.completion_tokens,
  171. total_quota = usage_statistics.total_quota + EXCLUDED.total_quota,
  172. updated_time = EXCLUDED.updated_time
  173. `, date, tokenId, tokenName, modelName, totalRequests, successfulRequests, failedRequests,
  174. totalTokens, promptTokens, completionTokens, totalQuota, now, now).Error
  175. } else {
  176. // SQLite - 使用 INSERT OR REPLACE
  177. var existing UsageStatistics
  178. err := DB.Where("date = ? AND token_id = ? AND model_name = ?", date, tokenId, modelName).First(&existing).Error
  179. if err == gorm.ErrRecordNotFound {
  180. // 记录不存在,直接插入
  181. newRecord := UsageStatistics{
  182. Date: date,
  183. TokenId: tokenId,
  184. TokenName: tokenName,
  185. ModelName: modelName,
  186. TotalRequests: totalRequests,
  187. SuccessfulRequests: successfulRequests,
  188. FailedRequests: failedRequests,
  189. TotalTokens: totalTokens,
  190. PromptTokens: promptTokens,
  191. CompletionTokens: completionTokens,
  192. TotalQuota: totalQuota,
  193. CreatedTime: now,
  194. UpdatedTime: now,
  195. }
  196. return DB.Create(&newRecord).Error
  197. } else if err != nil {
  198. return err
  199. } else {
  200. // 记录存在,更新数据
  201. updates := map[string]interface{}{
  202. "token_name": tokenName,
  203. "total_requests": existing.TotalRequests + totalRequests,
  204. "successful_requests": existing.SuccessfulRequests + successfulRequests,
  205. "failed_requests": existing.FailedRequests + failedRequests,
  206. "total_tokens": existing.TotalTokens + totalTokens,
  207. "prompt_tokens": existing.PromptTokens + promptTokens,
  208. "completion_tokens": existing.CompletionTokens + completionTokens,
  209. "total_quota": existing.TotalQuota + totalQuota,
  210. "updated_time": now,
  211. }
  212. return DB.Model(&existing).Updates(updates).Error
  213. }
  214. }
  215. }
  216. // RecordUsageStatistics 记录用量统计(从日志记录中调用)
  217. func RecordUsageStatistics(tokenId int, tokenName, modelName string,
  218. promptTokens, completionTokens int, quota int, isSuccess bool) error {
  219. if tokenId <= 0 || modelName == "" {
  220. return errors.New("invalid parameters for usage statistics")
  221. }
  222. date := time.Now().Format("2006-01-02")
  223. totalTokens := promptTokens + completionTokens
  224. totalRequests := 1
  225. successfulRequests := 0
  226. failedRequests := 0
  227. if isSuccess {
  228. successfulRequests = 1
  229. } else {
  230. failedRequests = 1
  231. }
  232. return UpsertUsageStatistics(date, tokenId, tokenName, modelName,
  233. totalRequests, successfulRequests, failedRequests,
  234. totalTokens, promptTokens, completionTokens, quota)
  235. }
  236. // GetUsageStatisticsSummary 获取用量统计摘要信息
  237. func GetUsageStatisticsSummary(startDate, endDate string, tokenId int, modelName string) (map[string]interface{}, error) {
  238. query := DB.Model(&UsageStatistics{})
  239. // 添加查询条件
  240. if startDate != "" {
  241. query = query.Where("date >= ?", startDate)
  242. }
  243. if endDate != "" {
  244. query = query.Where("date <= ?", endDate)
  245. }
  246. if tokenId > 0 {
  247. query = query.Where("token_id = ?", tokenId)
  248. }
  249. if modelName != "" {
  250. query = query.Where("model_name LIKE ?", "%"+modelName+"%")
  251. }
  252. var result struct {
  253. TotalRequests int `json:"total_requests"`
  254. SuccessfulRequests int `json:"successful_requests"`
  255. FailedRequests int `json:"failed_requests"`
  256. TotalTokens int `json:"total_tokens"`
  257. TotalQuota int `json:"total_quota"`
  258. }
  259. err := query.Select(
  260. "SUM(total_requests) as total_requests",
  261. "SUM(successful_requests) as successful_requests",
  262. "SUM(failed_requests) as failed_requests",
  263. "SUM(total_tokens) as total_tokens",
  264. "SUM(total_quota) as total_quota",
  265. ).Scan(&result).Error
  266. if err != nil {
  267. return nil, err
  268. }
  269. summary := map[string]interface{}{
  270. "total_requests": result.TotalRequests,
  271. "successful_requests": result.SuccessfulRequests,
  272. "failed_requests": result.FailedRequests,
  273. "success_rate": 0.0,
  274. "total_tokens": result.TotalTokens,
  275. "total_quota": result.TotalQuota,
  276. }
  277. if result.TotalRequests > 0 {
  278. summary["success_rate"] = float64(result.SuccessfulRequests) / float64(result.TotalRequests) * 100
  279. }
  280. return summary, nil
  281. }
  282. // GetMonthlyUsageStatisticsSummary 获取月度用量统计摘要信息
  283. func GetMonthlyUsageStatisticsSummary(startDate, endDate string, tokenId int, modelName string) (map[string]interface{}, error) {
  284. // 使用原生SQL查询实现按月分组统计
  285. db := DB.Model(&UsageStatistics{})
  286. // 构建查询条件
  287. conditions := ""
  288. params := []interface{}{}
  289. // 添加日期范围条件(按月查询)
  290. if startDate != "" {
  291. conditions += " AND date >= ?"
  292. params = append(params, startDate+"-01")
  293. }
  294. if endDate != "" {
  295. // 获取 endDate 所在月份的最后一天
  296. if len(endDate) >= 7 {
  297. year := endDate[0:4]
  298. month := endDate[5:7]
  299. conditions += " AND date <= ?"
  300. params = append(params, year+"-"+month+"-31")
  301. }
  302. }
  303. if tokenId > 0 {
  304. conditions += " AND token_id = ?"
  305. params = append(params, tokenId)
  306. }
  307. if modelName != "" {
  308. conditions += " AND model_name LIKE ?"
  309. params = append(params, "%"+modelName+"%")
  310. }
  311. // 构建完整的SQL查询
  312. sql := `
  313. SELECT
  314. SUM(total_requests) as total_requests,
  315. SUM(successful_requests) as successful_requests,
  316. SUM(failed_requests) as failed_requests,
  317. SUM(total_tokens) as total_tokens,
  318. SUM(total_quota) as total_quota
  319. FROM (
  320. SELECT
  321. SUM(total_requests) as total_requests,
  322. SUM(successful_requests) as successful_requests,
  323. SUM(failed_requests) as failed_requests,
  324. SUM(total_tokens) as total_tokens,
  325. SUM(total_quota) as total_quota
  326. FROM usage_statistics
  327. WHERE 1=1` + conditions + `
  328. GROUP BY SUBSTR(date, 1, 7), token_id, token_name, model_name
  329. ) as grouped_data
  330. `
  331. var result struct {
  332. TotalRequests int `json:"total_requests"`
  333. SuccessfulRequests int `json:"successful_requests"`
  334. FailedRequests int `json:"failed_requests"`
  335. TotalTokens int `json:"total_tokens"`
  336. TotalQuota int `json:"total_quota"`
  337. }
  338. err := db.Raw(sql, params...).Scan(&result).Error
  339. if err != nil {
  340. return nil, err
  341. }
  342. summary := map[string]interface{}{
  343. "total_requests": result.TotalRequests,
  344. "successful_requests": result.SuccessfulRequests,
  345. "failed_requests": result.FailedRequests,
  346. "success_rate": 0.0,
  347. "total_tokens": result.TotalTokens,
  348. "total_quota": result.TotalQuota,
  349. }
  350. if result.TotalRequests > 0 {
  351. summary["success_rate"] = float64(result.SuccessfulRequests) / float64(result.TotalRequests) * 100
  352. }
  353. return summary, nil
  354. }
  355. // GetUserUsageStatistics 获取特定用户的用量统计数据
  356. func GetUserUsageStatistics(userId int, startDate, endDate string, tokenId int, modelName string, page, pageSize int) ([]*UsageStatistics, int64, error) {
  357. var statistics []*UsageStatistics
  358. var total int64
  359. // 基本查询,需要通过token表连接过滤用户
  360. query := DB.Table("usage_statistics").
  361. Joins("JOIN tokens ON usage_statistics.token_id = tokens.id").
  362. Where("tokens.user_id = ?", userId)
  363. // 添加查询条件
  364. if startDate != "" {
  365. query = query.Where("usage_statistics.date >= ?", startDate)
  366. }
  367. if endDate != "" {
  368. query = query.Where("usage_statistics.date <= ?", endDate)
  369. }
  370. if tokenId > 0 {
  371. query = query.Where("usage_statistics.token_id = ?", tokenId)
  372. }
  373. if modelName != "" {
  374. query = query.Where("usage_statistics.model_name LIKE ?", "%"+modelName+"%")
  375. }
  376. // 获取总数
  377. err := query.Count(&total).Error
  378. if err != nil {
  379. return nil, 0, err
  380. }
  381. // 分页查询
  382. offset := (page - 1) * pageSize
  383. err = query.Select("usage_statistics.*").
  384. Order("usage_statistics.date DESC, usage_statistics.token_id ASC, usage_statistics.model_name ASC").
  385. Offset(offset).Limit(pageSize).Find(&statistics).Error
  386. return statistics, total, err
  387. }
  388. // GetUserMonthlyUsageStatistics 获取特定用户的月度用量统计数据
  389. func GetUserMonthlyUsageStatistics(userId int, startDate, endDate string, tokenId int, modelName string, page, pageSize int) ([]*UsageStatistics, int64, error) {
  390. var statistics []*UsageStatistics
  391. var total int64
  392. // 使用原生SQL查询实现按月分组统计
  393. db := DB.Table("usage_statistics").
  394. Joins("JOIN tokens ON usage_statistics.token_id = tokens.id").
  395. Where("tokens.user_id = ?", userId)
  396. // 构建查询条件
  397. conditions := " AND tokens.user_id = ?"
  398. params := []interface{}{userId}
  399. // 添加日期范围条件(按月查询)
  400. if startDate != "" {
  401. conditions += " AND usage_statistics.date >= ?"
  402. params = append(params, startDate+"-01")
  403. }
  404. if endDate != "" {
  405. // 获取 endDate 所在月份的最后一天
  406. if len(endDate) >= 7 {
  407. year := endDate[0:4]
  408. month := endDate[5:7]
  409. conditions += " AND usage_statistics.date <= ?"
  410. params = append(params, year+"-"+month+"-31")
  411. }
  412. }
  413. if tokenId > 0 {
  414. conditions += " AND usage_statistics.token_id = ?"
  415. params = append(params, tokenId)
  416. }
  417. if modelName != "" {
  418. conditions += " AND usage_statistics.model_name LIKE ?"
  419. params = append(params, "%"+modelName+"%")
  420. }
  421. // 构建完整的SQL查询
  422. sql := `
  423. SELECT
  424. MAX(usage_statistics.id) as id,
  425. SUBSTR(usage_statistics.date, 1, 7) as date,
  426. usage_statistics.token_id,
  427. usage_statistics.token_name,
  428. usage_statistics.model_name,
  429. SUM(usage_statistics.total_requests) as total_requests,
  430. SUM(usage_statistics.successful_requests) as successful_requests,
  431. SUM(usage_statistics.failed_requests) as failed_requests,
  432. SUM(usage_statistics.total_tokens) as total_tokens,
  433. SUM(usage_statistics.prompt_tokens) as prompt_tokens,
  434. SUM(usage_statistics.completion_tokens) as completion_tokens,
  435. SUM(usage_statistics.total_quota) as total_quota,
  436. MAX(usage_statistics.created_time) as created_time,
  437. MAX(usage_statistics.updated_time) as updated_time
  438. FROM usage_statistics
  439. JOIN tokens ON usage_statistics.token_id = tokens.id
  440. WHERE 1=1` + conditions + `
  441. GROUP BY SUBSTR(usage_statistics.date, 1, 7), usage_statistics.token_id, usage_statistics.token_name, usage_statistics.model_name
  442. ORDER BY date DESC, token_id ASC, model_name ASC
  443. `
  444. // 获取总数
  445. countSQL := `
  446. SELECT COUNT(*) as count FROM (
  447. SELECT 1
  448. FROM usage_statistics
  449. JOIN tokens ON usage_statistics.token_id = tokens.id
  450. WHERE 1=1` + conditions + `
  451. GROUP BY SUBSTR(usage_statistics.date, 1, 7), usage_statistics.token_id, usage_statistics.token_name, usage_statistics.model_name
  452. ) as grouped_data
  453. `
  454. var countResult struct {
  455. Count int64 `json:"count"`
  456. }
  457. err := db.Raw(countSQL, params...).Scan(&countResult).Error
  458. if err != nil {
  459. return nil, 0, err
  460. }
  461. total = countResult.Count
  462. // 分页查询
  463. offset := (page - 1) * pageSize
  464. limitSQL := sql + " LIMIT ? OFFSET ?"
  465. params = append(params, pageSize, offset)
  466. err = db.Raw(limitSQL, params...).Scan(&statistics).Error
  467. return statistics, total, err
  468. }
  469. // GetUserUsageStatisticsSummary 获取特定用户的用量统计摘要信息
  470. func GetUserUsageStatisticsSummary(userId int, startDate, endDate string, tokenId int, modelName string) (map[string]interface{}, error) {
  471. query := DB.Table("usage_statistics").
  472. Joins("JOIN tokens ON usage_statistics.token_id = tokens.id").
  473. Where("tokens.user_id = ?", userId)
  474. // 添加查询条件
  475. if startDate != "" {
  476. query = query.Where("usage_statistics.date >= ?", startDate)
  477. }
  478. if endDate != "" {
  479. query = query.Where("usage_statistics.date <= ?", endDate)
  480. }
  481. if tokenId > 0 {
  482. query = query.Where("usage_statistics.token_id = ?", tokenId)
  483. }
  484. if modelName != "" {
  485. query = query.Where("usage_statistics.model_name LIKE ?", "%"+modelName+"%")
  486. }
  487. var result struct {
  488. TotalRequests int `json:"total_requests"`
  489. SuccessfulRequests int `json:"successful_requests"`
  490. FailedRequests int `json:"failed_requests"`
  491. TotalTokens int `json:"total_tokens"`
  492. TotalQuota int `json:"total_quota"`
  493. }
  494. err := query.Select(
  495. "SUM(usage_statistics.total_requests) as total_requests",
  496. "SUM(usage_statistics.successful_requests) as successful_requests",
  497. "SUM(usage_statistics.failed_requests) as failed_requests",
  498. "SUM(usage_statistics.total_tokens) as total_tokens",
  499. "SUM(usage_statistics.total_quota) as total_quota",
  500. ).Scan(&result).Error
  501. if err != nil {
  502. return nil, err
  503. }
  504. summary := map[string]interface{}{
  505. "total_requests": result.TotalRequests,
  506. "successful_requests": result.SuccessfulRequests,
  507. "failed_requests": result.FailedRequests,
  508. "success_rate": 0.0,
  509. "total_tokens": result.TotalTokens,
  510. "total_quota": result.TotalQuota,
  511. }
  512. if result.TotalRequests > 0 {
  513. summary["success_rate"] = float64(result.SuccessfulRequests) / float64(result.TotalRequests) * 100
  514. }
  515. return summary, nil
  516. }
  517. // GetUserMonthlyUsageStatisticsSummary 获取特定用户的月度用量统计摘要信息
  518. func GetUserMonthlyUsageStatisticsSummary(userId int, startDate, endDate string, tokenId int, modelName string) (map[string]interface{}, error) {
  519. // 使用原生SQL查询实现按月分组统计
  520. db := DB.Table("usage_statistics").
  521. Joins("JOIN tokens ON usage_statistics.token_id = tokens.id").
  522. Where("tokens.user_id = ?", userId)
  523. // 构建查询条件
  524. conditions := " AND tokens.user_id = ?"
  525. params := []interface{}{userId}
  526. // 添加日期范围条件(按月查询)
  527. if startDate != "" {
  528. conditions += " AND usage_statistics.date >= ?"
  529. params = append(params, startDate+"-01")
  530. }
  531. if endDate != "" {
  532. // 获取 endDate 所在月份的最后一天
  533. if len(endDate) >= 7 {
  534. year := endDate[0:4]
  535. month := endDate[5:7]
  536. conditions += " AND usage_statistics.date <= ?"
  537. params = append(params, year+"-"+month+"-31")
  538. }
  539. }
  540. if tokenId > 0 {
  541. conditions += " AND usage_statistics.token_id = ?"
  542. params = append(params, tokenId)
  543. }
  544. if modelName != "" {
  545. conditions += " AND usage_statistics.model_name LIKE ?"
  546. params = append(params, "%"+modelName+"%")
  547. }
  548. // 构建完整的SQL查询
  549. sql := `
  550. SELECT
  551. SUM(total_requests) as total_requests,
  552. SUM(successful_requests) as successful_requests,
  553. SUM(failed_requests) as failed_requests,
  554. SUM(total_tokens) as total_tokens,
  555. SUM(total_quota) as total_quota
  556. FROM (
  557. SELECT
  558. SUM(usage_statistics.total_requests) as total_requests,
  559. SUM(usage_statistics.successful_requests) as successful_requests,
  560. SUM(usage_statistics.failed_requests) as failed_requests,
  561. SUM(usage_statistics.total_tokens) as total_tokens,
  562. SUM(usage_statistics.total_quota) as total_quota
  563. FROM usage_statistics
  564. JOIN tokens ON usage_statistics.token_id = tokens.id
  565. WHERE 1=1` + conditions + `
  566. GROUP BY SUBSTR(usage_statistics.date, 1, 7), usage_statistics.token_id, usage_statistics.token_name, usage_statistics.model_name
  567. ) as grouped_data
  568. `
  569. var result struct {
  570. TotalRequests int `json:"total_requests"`
  571. SuccessfulRequests int `json:"successful_requests"`
  572. FailedRequests int `json:"failed_requests"`
  573. TotalTokens int `json:"total_tokens"`
  574. TotalQuota int `json:"total_quota"`
  575. }
  576. err := db.Raw(sql, params...).Scan(&result).Error
  577. if err != nil {
  578. return nil, err
  579. }
  580. summary := map[string]interface{}{
  581. "total_requests": result.TotalRequests,
  582. "successful_requests": result.SuccessfulRequests,
  583. "failed_requests": result.FailedRequests,
  584. "success_rate": 0.0,
  585. "total_tokens": result.TotalTokens,
  586. "total_quota": result.TotalQuota,
  587. }
  588. if result.TotalRequests > 0 {
  589. summary["success_rate"] = float64(result.SuccessfulRequests) / float64(result.TotalRequests) * 100
  590. }
  591. return summary, nil
  592. }