statistics_charts.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. package model
  2. import (
  3. "one-api/common"
  4. "strconv"
  5. "time"
  6. )
  7. // ChannelStatistics 按渠道统计的数据结构
  8. type ChannelStatistics struct {
  9. Time string `json:"time"`
  10. ChannelId int `json:"channel_id"`
  11. ChannelName string `json:"channel_name"`
  12. Quota int `json:"quota"`
  13. Count int `json:"count"`
  14. }
  15. // TokenStatistics 按令牌统计的数据结构
  16. type TokenStatistics struct {
  17. Time string `json:"time"`
  18. TokenName string `json:"token_name"`
  19. Quota int `json:"quota"`
  20. Count int `json:"count"`
  21. }
  22. // UserStatistics 按用户统计的数据结构
  23. type UserStatistics struct {
  24. Time string `json:"time"`
  25. Username string `json:"username"`
  26. Quota int `json:"quota"`
  27. Count int `json:"count"`
  28. }
  29. // GetChannelStatistics 获取按渠道统计的数据
  30. func GetChannelStatistics(startTimestamp, endTimestamp int, username, tokenName, modelName string, channel int, group, defaultTime string) ([]ChannelStatistics, error) {
  31. var statistics []ChannelStatistics
  32. // 构建查询条件
  33. tx := LOG_DB.Table("logs").Select(`
  34. created_at,
  35. channel_id,
  36. COUNT(*) as count,
  37. SUM(quota) as quota
  38. `).Where("type = ?", 2) // LogTypeConsume = 2
  39. if username != "" {
  40. tx = tx.Where("username = ?", username)
  41. }
  42. if tokenName != "" {
  43. tx = tx.Where("token_name = ?", tokenName)
  44. }
  45. if modelName != "" {
  46. tx = tx.Where("model_name LIKE ?", "%"+modelName+"%")
  47. }
  48. if channel != 0 {
  49. tx = tx.Where("channel_id = ?", channel)
  50. }
  51. if group != "" {
  52. tx = tx.Where("`group` = ?", group)
  53. }
  54. if startTimestamp != 0 {
  55. tx = tx.Where("created_at >= ?", startTimestamp)
  56. }
  57. if endTimestamp != 0 {
  58. tx = tx.Where("created_at <= ?", endTimestamp)
  59. }
  60. // 按时间粒度分组 - 使用数据库无关的函数
  61. var groupBy string
  62. switch defaultTime {
  63. case "hour":
  64. if common.UsingMySQL {
  65. groupBy = "channel_id, DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%m-%d %H:00:00')"
  66. } else if common.UsingPostgreSQL {
  67. groupBy = "channel_id, TO_CHAR(TO_TIMESTAMP(created_at), 'YYYY-MM-DD HH24:00:00')"
  68. } else {
  69. // SQLite
  70. groupBy = "channel_id, strftime('%Y-%m-%d %H:00:00', datetime(created_at, 'unixepoch'))"
  71. }
  72. case "day":
  73. if common.UsingMySQL {
  74. groupBy = "channel_id, DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%m-%d')"
  75. } else if common.UsingPostgreSQL {
  76. groupBy = "channel_id, TO_CHAR(TO_TIMESTAMP(created_at), 'YYYY-MM-DD')"
  77. } else {
  78. // SQLite
  79. groupBy = "channel_id, strftime('%Y-%m-%d', datetime(created_at, 'unixepoch'))"
  80. }
  81. case "week":
  82. if common.UsingMySQL {
  83. groupBy = "channel_id, DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%U')"
  84. } else if common.UsingPostgreSQL {
  85. groupBy = "channel_id, TO_CHAR(TO_TIMESTAMP(created_at), 'YYYY-IW')"
  86. } else {
  87. // SQLite
  88. groupBy = "channel_id, strftime('%Y-%W', datetime(created_at, 'unixepoch'))"
  89. }
  90. default:
  91. if common.UsingMySQL {
  92. groupBy = "channel_id, DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%m-%d')"
  93. } else if common.UsingPostgreSQL {
  94. groupBy = "channel_id, TO_CHAR(TO_TIMESTAMP(created_at), 'YYYY-MM-DD')"
  95. } else {
  96. // SQLite
  97. groupBy = "channel_id, strftime('%Y-%m-%d', datetime(created_at, 'unixepoch'))"
  98. }
  99. }
  100. // 按渠道和时间分组
  101. tx = tx.Group(groupBy).Order("MIN(created_at) ASC")
  102. // 执行查询
  103. var results []struct {
  104. CreatedAt int64 `json:"created_at"`
  105. ChannelId int `json:"channel_id"`
  106. Count int `json:"count"`
  107. Quota int `json:"quota"`
  108. }
  109. err := tx.Scan(&results).Error
  110. if err != nil {
  111. return nil, err
  112. }
  113. // 获取渠道名称映射
  114. channelIds := make([]int, 0)
  115. channelMap := make(map[int]string)
  116. for _, result := range results {
  117. if result.ChannelId != 0 {
  118. channelIds = append(channelIds, result.ChannelId)
  119. }
  120. }
  121. if len(channelIds) > 0 {
  122. var channels []struct {
  123. Id int `gorm:"column:id"`
  124. Name string `gorm:"column:name"`
  125. }
  126. if err = DB.Table("channels").Select("id, name").Where("id IN ?", channelIds).Find(&channels).Error; err != nil {
  127. return nil, err
  128. }
  129. for _, channel := range channels {
  130. channelMap[channel.Id] = channel.Name
  131. }
  132. }
  133. // 格式化时间并构建返回结果
  134. for _, result := range results {
  135. timeStr := formatTime(result.CreatedAt, defaultTime)
  136. statistics = append(statistics, ChannelStatistics{
  137. Time: timeStr,
  138. ChannelId: result.ChannelId,
  139. ChannelName: channelMap[result.ChannelId],
  140. Quota: result.Quota,
  141. Count: result.Count,
  142. })
  143. }
  144. return statistics, nil
  145. }
  146. // GetTokenStatistics 获取按令牌统计的数据
  147. func GetTokenStatistics(startTimestamp, endTimestamp int, username, tokenName, modelName string, channel int, group, defaultTime string) ([]TokenStatistics, error) {
  148. var statistics []TokenStatistics
  149. // 构建查询条件
  150. tx := LOG_DB.Table("logs").Select(`
  151. created_at,
  152. token_name,
  153. COUNT(*) as count,
  154. SUM(quota) as quota
  155. `).Where("type = ?", 2) // LogTypeConsume = 2
  156. if username != "" {
  157. tx = tx.Where("username = ?", username)
  158. }
  159. if tokenName != "" {
  160. tx = tx.Where("token_name = ?", tokenName)
  161. }
  162. if modelName != "" {
  163. tx = tx.Where("model_name LIKE ?", "%"+modelName+"%")
  164. }
  165. if channel != 0 {
  166. tx = tx.Where("channel_id = ?", channel)
  167. }
  168. if group != "" {
  169. tx = tx.Where("`group` = ?", group)
  170. }
  171. if startTimestamp != 0 {
  172. tx = tx.Where("created_at >= ?", startTimestamp)
  173. }
  174. if endTimestamp != 0 {
  175. tx = tx.Where("created_at <= ?", endTimestamp)
  176. }
  177. // 按时间粒度分组 - 使用数据库无关的函数
  178. var groupBy string
  179. switch defaultTime {
  180. case "hour":
  181. if common.UsingMySQL {
  182. groupBy = "token_name, DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%m-%d %H:00:00')"
  183. } else if common.UsingPostgreSQL {
  184. groupBy = "token_name, TO_CHAR(TO_TIMESTAMP(created_at), 'YYYY-MM-DD HH24:00:00')"
  185. } else {
  186. // SQLite
  187. groupBy = "token_name, strftime('%Y-%m-%d %H:00:00', datetime(created_at, 'unixepoch'))"
  188. }
  189. case "day":
  190. if common.UsingMySQL {
  191. groupBy = "token_name, DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%m-%d')"
  192. } else if common.UsingPostgreSQL {
  193. groupBy = "token_name, TO_CHAR(TO_TIMESTAMP(created_at), 'YYYY-MM-DD')"
  194. } else {
  195. // SQLite
  196. groupBy = "token_name, strftime('%Y-%m-%d', datetime(created_at, 'unixepoch'))"
  197. }
  198. case "week":
  199. if common.UsingMySQL {
  200. groupBy = "token_name, DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%U')"
  201. } else if common.UsingPostgreSQL {
  202. groupBy = "token_name, TO_CHAR(TO_TIMESTAMP(created_at), 'YYYY-IW')"
  203. } else {
  204. // SQLite
  205. groupBy = "token_name, strftime('%Y-%W', datetime(created_at, 'unixepoch'))"
  206. }
  207. default:
  208. if common.UsingMySQL {
  209. groupBy = "token_name, DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%m-%d')"
  210. } else if common.UsingPostgreSQL {
  211. groupBy = "token_name, TO_CHAR(TO_TIMESTAMP(created_at), 'YYYY-MM-DD')"
  212. } else {
  213. // SQLite
  214. groupBy = "token_name, strftime('%Y-%m-%d', datetime(created_at, 'unixepoch'))"
  215. }
  216. }
  217. // 按令牌和时间分组
  218. tx = tx.Group(groupBy).Order("MIN(created_at) ASC")
  219. // 执行查询
  220. var results []struct {
  221. CreatedAt int64 `json:"created_at"`
  222. TokenName string `json:"token_name"`
  223. Count int `json:"count"`
  224. Quota int `json:"quota"`
  225. }
  226. err := tx.Scan(&results).Error
  227. if err != nil {
  228. return nil, err
  229. }
  230. // 格式化时间并构建返回结果
  231. for _, result := range results {
  232. timeStr := formatTime(result.CreatedAt, defaultTime)
  233. statistics = append(statistics, TokenStatistics{
  234. Time: timeStr,
  235. TokenName: result.TokenName,
  236. Quota: result.Quota,
  237. Count: result.Count,
  238. })
  239. }
  240. return statistics, nil
  241. }
  242. // GetUserStatistics 获取按用户统计的数据
  243. func GetUserStatistics(startTimestamp, endTimestamp int, username, tokenName, modelName string, channel int, group, defaultTime string) ([]UserStatistics, error) {
  244. var statistics []UserStatistics
  245. // 构建查询条件
  246. tx := LOG_DB.Table("logs").Select(`
  247. created_at,
  248. username,
  249. COUNT(*) as count,
  250. SUM(quota) as quota
  251. `).Where("type = ?", 2) // LogTypeConsume = 2
  252. if username != "" {
  253. tx = tx.Where("username = ?", username)
  254. }
  255. if tokenName != "" {
  256. tx = tx.Where("token_name = ?", tokenName)
  257. }
  258. if modelName != "" {
  259. tx = tx.Where("model_name LIKE ?", "%"+modelName+"%")
  260. }
  261. if channel != 0 {
  262. tx = tx.Where("channel_id = ?", channel)
  263. }
  264. if group != "" {
  265. tx = tx.Where("`group` = ?", group)
  266. }
  267. if startTimestamp != 0 {
  268. tx = tx.Where("created_at >= ?", startTimestamp)
  269. }
  270. if endTimestamp != 0 {
  271. tx = tx.Where("created_at <= ?", endTimestamp)
  272. }
  273. // 按时间粒度分组 - 使用数据库无关的函数
  274. var groupBy string
  275. switch defaultTime {
  276. case "hour":
  277. if common.UsingMySQL {
  278. groupBy = "username, DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%m-%d %H:00:00')"
  279. } else if common.UsingPostgreSQL {
  280. groupBy = "username, TO_CHAR(TO_TIMESTAMP(created_at), 'YYYY-MM-DD HH24:00:00')"
  281. } else {
  282. // SQLite
  283. groupBy = "username, strftime('%Y-%m-%d %H:00:00', datetime(created_at, 'unixepoch'))"
  284. }
  285. case "day":
  286. if common.UsingMySQL {
  287. groupBy = "username, DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%m-%d')"
  288. } else if common.UsingPostgreSQL {
  289. groupBy = "username, TO_CHAR(TO_TIMESTAMP(created_at), 'YYYY-MM-DD')"
  290. } else {
  291. // SQLite
  292. groupBy = "username, strftime('%Y-%m-%d', datetime(created_at, 'unixepoch'))"
  293. }
  294. case "week":
  295. if common.UsingMySQL {
  296. groupBy = "username, DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%U')"
  297. } else if common.UsingPostgreSQL {
  298. groupBy = "username, TO_CHAR(TO_TIMESTAMP(created_at), 'YYYY-IW')"
  299. } else {
  300. // SQLite
  301. groupBy = "username, strftime('%Y-%W', datetime(created_at, 'unixepoch'))"
  302. }
  303. default:
  304. if common.UsingMySQL {
  305. groupBy = "username, DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%m-%d')"
  306. } else if common.UsingPostgreSQL {
  307. groupBy = "username, TO_CHAR(TO_TIMESTAMP(created_at), 'YYYY-MM-DD')"
  308. } else {
  309. // SQLite
  310. groupBy = "username, strftime('%Y-%m-%d', datetime(created_at, 'unixepoch'))"
  311. }
  312. }
  313. // 按用户和时间分组
  314. tx = tx.Group(groupBy).Order("MIN(created_at) ASC")
  315. // 执行查询
  316. var results []struct {
  317. CreatedAt int64 `json:"created_at"`
  318. Username string `json:"username"`
  319. Count int `json:"count"`
  320. Quota int `json:"quota"`
  321. }
  322. err := tx.Scan(&results).Error
  323. if err != nil {
  324. return nil, err
  325. }
  326. // 格式化时间并构建返回结果
  327. for _, result := range results {
  328. timeStr := formatTime(result.CreatedAt, defaultTime)
  329. statistics = append(statistics, UserStatistics{
  330. Time: timeStr,
  331. Username: result.Username,
  332. Quota: result.Quota,
  333. Count: result.Count,
  334. })
  335. }
  336. return statistics, nil
  337. }
  338. // formatTime 根据时间粒度格式化时间
  339. func formatTime(timestamp int64, defaultTime string) string {
  340. t := time.Unix(timestamp, 0)
  341. switch defaultTime {
  342. case "hour":
  343. return t.Format("2006-01-02 15:00")
  344. case "day":
  345. return t.Format("2006-01-02")
  346. case "week":
  347. _, week := t.ISOWeek()
  348. return t.Format("2006-01") + "-W" + strconv.Itoa(week)
  349. default:
  350. return t.Format("2006-01-02")
  351. }
  352. }