Skip to content

Commit f1e0876

Browse files
committed
update1.2
1 parent 276ee28 commit f1e0876

12 files changed

Lines changed: 478 additions & 37 deletions

File tree

VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v1.2

common/constants.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ var TopUpLink = ""
1818

1919
// var ChatLink = ""
2020
// var ChatLink2 = ""
21-
var QuotaPerUnit = 500 * 1000.0 // $0.002 / 1K tokens
22-
var DisplayInCurrencyEnabled = true
21+
var QuotaPerUnit = 500 * 1000.0 // $0.002 / 1K tokens
22+
var DisplayInCurrencyEnabled = false // true
2323
var DisplayTokenStatEnabled = true
2424
var DrawingEnabled = true
2525
var TaskEnabled = true
@@ -71,6 +71,7 @@ var DebugEnabled bool
7171
var MemoryCacheEnabled bool
7272

7373
var LogConsumeEnabled = true
74+
var LogUserInputEnabled = true
7475

7576
var SMTPServer = ""
7677
var SMTPPort = 587

controller/channel_export.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package controller
2+
3+
import (
4+
"bytes"
5+
"net/http"
6+
"one-api/common"
7+
"one-api/model"
8+
9+
"github.com/gin-gonic/gin"
10+
"github.com/xuri/excelize/v2"
11+
)
12+
13+
func ExportChannels(c *gin.Context) {
14+
// 获取所有渠道数据
15+
channels, err := model.GetAllChannels(0, 0, true, false)
16+
if err != nil {
17+
common.ApiError(c, err)
18+
return
19+
}
20+
21+
// 创建Excel文件
22+
f := excelize.NewFile()
23+
defer func() {
24+
if err := f.Close(); err != nil {
25+
common.SysError("Error closing Excel file: " + err.Error())
26+
}
27+
}()
28+
29+
// 创建工作表
30+
sheetName := "Channels"
31+
f.SetSheetName("Sheet1", sheetName)
32+
33+
// 设置表头
34+
headers := []string{
35+
"ID", "名称", "类型", "状态", "密钥", "组织", "测试模型",
36+
"权重", "创建时间", "测试时间", "响应时间", "基础URL", "其他",
37+
"余额", "余额更新时间", "模型", "分组", "已用配额",
38+
"模型映射", "状态码映射", "优先级", "自动禁用", "标签", "额外设置", "参数覆盖",
39+
}
40+
41+
for i, header := range headers {
42+
cell, _ := excelize.CoordinatesToCellName(i+1, 1)
43+
f.SetCellValue(sheetName, cell, header)
44+
}
45+
46+
// 填充数据
47+
for i, channel := range channels {
48+
row := i + 2 // 从第二行开始填充数据
49+
data := []interface{}{
50+
channel.Id,
51+
channel.Name,
52+
channel.Type,
53+
channel.Status,
54+
channel.Key,
55+
channel.OpenAIOrganization,
56+
channel.TestModel,
57+
channel.Weight,
58+
channel.CreatedTime,
59+
channel.TestTime,
60+
channel.ResponseTime,
61+
channel.BaseURL,
62+
channel.Other,
63+
channel.Balance,
64+
channel.BalanceUpdatedTime,
65+
channel.Models,
66+
channel.Group,
67+
channel.UsedQuota,
68+
channel.ModelMapping,
69+
channel.StatusCodeMapping,
70+
channel.Priority,
71+
channel.AutoBan,
72+
channel.Tag,
73+
channel.Setting,
74+
channel.ParamOverride,
75+
}
76+
77+
for j, value := range data {
78+
cell, _ := excelize.CoordinatesToCellName(j+1, row)
79+
f.SetCellValue(sheetName, cell, value)
80+
}
81+
}
82+
83+
// 设置响应头
84+
c.Header("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
85+
c.Header("Content-Disposition", "attachment; filename=channels.xlsx")
86+
c.Header("Cache-Control", "no-cache")
87+
88+
// 将Excel文件写入缓冲区
89+
var buf bytes.Buffer
90+
if err := f.Write(&buf); err != nil {
91+
common.ApiError(c, err)
92+
return
93+
}
94+
95+
// 返回Excel文件
96+
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", buf.Bytes())
97+
}

controller/channel_import.go

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
package controller
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"one-api/common"
7+
"one-api/model"
8+
"strconv"
9+
10+
// "strings"
11+
12+
"github.com/gin-gonic/gin"
13+
"github.com/xuri/excelize/v2"
14+
)
15+
16+
func ImportChannels(c *gin.Context) {
17+
// 从请求中获取上传的文件
18+
file, err := c.FormFile("file")
19+
if err != nil {
20+
common.ApiError(c, fmt.Errorf("failed to get file: %w", err))
21+
return
22+
}
23+
24+
// 打开文件
25+
src, err := file.Open()
26+
if err != nil {
27+
common.ApiError(c, fmt.Errorf("failed to open file: %w", err))
28+
return
29+
}
30+
defer src.Close()
31+
32+
// 读取Excel文件
33+
f, err := excelize.OpenReader(src)
34+
if err != nil {
35+
common.ApiError(c, fmt.Errorf("failed to open Excel file: %w", err))
36+
return
37+
}
38+
defer func() {
39+
if err := f.Close(); err != nil {
40+
common.SysError("Error closing Excel file: " + err.Error())
41+
}
42+
}()
43+
44+
// 获取第一个工作表
45+
sheetName := f.GetSheetName(0)
46+
if sheetName == "" {
47+
common.ApiError(c, fmt.Errorf("no sheets found in Excel file"))
48+
return
49+
}
50+
51+
// 读取所有行
52+
rows, err := f.GetRows(sheetName)
53+
if err != nil {
54+
common.ApiError(c, fmt.Errorf("failed to read rows: %w", err))
55+
return
56+
}
57+
58+
// 检查是否有数据
59+
if len(rows) <= 1 {
60+
common.ApiError(c, fmt.Errorf("no data found in Excel file"))
61+
return
62+
}
63+
64+
// 解析表头
65+
headers := rows[0]
66+
headerMap := make(map[string]int)
67+
for i, header := range headers {
68+
headerMap[header] = i
69+
}
70+
71+
// 解析数据行
72+
channels := make([]model.Channel, 0, len(rows)-1)
73+
for i := 1; i < len(rows); i++ {
74+
row := rows[i]
75+
if len(row) == 0 {
76+
continue
77+
}
78+
79+
// 创建渠道对象
80+
channel := model.Channel{}
81+
82+
// 根据表头映射填充数据
83+
for header, colIndex := range headerMap {
84+
if colIndex >= len(row) {
85+
continue
86+
}
87+
value := row[colIndex]
88+
89+
switch header {
90+
case "ID":
91+
// ID由数据库自动生成,不需要设置
92+
case "名称":
93+
channel.Name = value
94+
case "类型":
95+
if v, err := strconv.Atoi(value); err == nil {
96+
channel.Type = v
97+
}
98+
case "状态":
99+
if v, err := strconv.Atoi(value); err == nil {
100+
channel.Status = v
101+
}
102+
case "密钥":
103+
channel.Key = value
104+
case "组织":
105+
if value != "" {
106+
channel.OpenAIOrganization = &value
107+
}
108+
case "测试模型":
109+
if value != "" {
110+
channel.TestModel = &value
111+
}
112+
case "权重":
113+
if value != "" {
114+
if v, err := strconv.ParseUint(value, 10, 32); err == nil {
115+
weight := uint(v)
116+
channel.Weight = &weight
117+
}
118+
}
119+
case "创建时间":
120+
if v, err := strconv.ParseInt(value, 10, 64); err == nil {
121+
channel.CreatedTime = v
122+
}
123+
case "测试时间":
124+
if v, err := strconv.ParseInt(value, 10, 64); err == nil {
125+
channel.TestTime = v
126+
}
127+
case "响应时间":
128+
if v, err := strconv.Atoi(value); err == nil {
129+
channel.ResponseTime = v
130+
}
131+
case "基础URL":
132+
if value != "" {
133+
channel.BaseURL = &value
134+
}
135+
case "其他":
136+
channel.Other = value
137+
case "余额":
138+
if v, err := strconv.ParseFloat(value, 64); err == nil {
139+
channel.Balance = v
140+
}
141+
case "余额更新时间":
142+
if v, err := strconv.ParseInt(value, 10, 64); err == nil {
143+
channel.BalanceUpdatedTime = v
144+
}
145+
case "模型":
146+
channel.Models = value
147+
case "分组":
148+
channel.Group = value
149+
case "已用配额":
150+
if v, err := strconv.ParseInt(value, 10, 64); err == nil {
151+
channel.UsedQuota = v
152+
}
153+
case "模型映射":
154+
if value != "" {
155+
channel.ModelMapping = &value
156+
}
157+
case "状态码映射":
158+
if value != "" {
159+
channel.StatusCodeMapping = &value
160+
}
161+
case "优先级":
162+
if value != "" {
163+
if v, err := strconv.ParseInt(value, 10, 64); err == nil {
164+
channel.Priority = &v
165+
}
166+
}
167+
case "自动禁用":
168+
if value != "" {
169+
if v, err := strconv.Atoi(value); err == nil {
170+
channel.AutoBan = &v
171+
}
172+
}
173+
case "标签":
174+
if value != "" {
175+
channel.Tag = &value
176+
}
177+
case "额外设置":
178+
if value != "" {
179+
channel.Setting = &value
180+
}
181+
case "参数覆盖":
182+
if value != "" {
183+
channel.ParamOverride = &value
184+
}
185+
}
186+
}
187+
188+
// 设置默认值
189+
if channel.CreatedTime == 0 {
190+
channel.CreatedTime = common.GetTimestamp()
191+
}
192+
193+
// 如果没有设置状态,默认为启用
194+
if channel.Status == 0 {
195+
channel.Status = 1
196+
}
197+
198+
channels = append(channels, channel)
199+
}
200+
201+
// 批量插入渠道
202+
err = model.BatchInsertChannels(channels)
203+
if err != nil {
204+
common.ApiError(c, fmt.Errorf("failed to insert channels: %w", err))
205+
return
206+
}
207+
208+
// 初始化渠道缓存
209+
model.InitChannelCache()
210+
211+
// 返回成功响应
212+
c.JSON(http.StatusOK, gin.H{
213+
"success": true,
214+
"message": fmt.Sprintf("成功导入 %d 个渠道", len(channels)),
215+
})
216+
}

controller/log.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,15 +146,30 @@ func GetLogsSelfStat(c *gin.Context) {
146146
}
147147

148148
func DeleteHistoryLogs(c *gin.Context) {
149-
targetTimestamp, _ := strconv.ParseInt(c.Query("target_timestamp"), 10, 64)
150-
if targetTimestamp == 0 {
149+
// 获取开始和结束时间戳参数
150+
startTimestamp, _ := strconv.ParseInt(c.Query("start_timestamp"), 10, 64)
151+
endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
152+
153+
// 兼容旧的target_timestamp参数
154+
if startTimestamp == 0 && endTimestamp == 0 {
155+
targetTimestamp, _ := strconv.ParseInt(c.Query("target_timestamp"), 10, 64)
156+
if targetTimestamp != 0 {
157+
// 如果只提供了target_timestamp,则将其作为结束时间,开始时间为0(删除该时间之前的所有日志)
158+
endTimestamp = targetTimestamp
159+
}
160+
}
161+
162+
// 验证参数
163+
if startTimestamp == 0 && endTimestamp == 0 {
151164
c.JSON(http.StatusOK, gin.H{
152165
"success": false,
153-
"message": "target timestamp is required",
166+
"message": "start timestamp or end timestamp is required",
154167
})
155168
return
156169
}
157-
count, err := model.DeleteOldLog(c.Request.Context(), targetTimestamp, 100)
170+
171+
// 调用模型层函数删除日志
172+
count, err := model.DeleteLogsByTimeRange(c.Request.Context(), startTimestamp, endTimestamp, 100)
158173
if err != nil {
159174
common.ApiError(c, err)
160175
return

0 commit comments

Comments
 (0)