From 88ba8a840ee8d806294b2f20e61a1ed7a514361a Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Sat, 3 Aug 2024 01:28:18 +0800 Subject: [PATCH 01/22] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E5=85=85?= =?UTF-8?q?=E5=80=BC=E8=AE=A2=E5=8D=95=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/topup.go | 4 ++-- controller/user.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/controller/topup.go b/controller/topup.go index 87c68c3..dc1e265 100644 --- a/controller/topup.go +++ b/controller/topup.go @@ -101,8 +101,8 @@ func RequestEpay(c *gin.Context) { } uri, params, err := client.Purchase(&epay.PurchaseArgs{ Type: payType, - ServiceTradeNo: "A" + tradeNo, - Name: "B" + tradeNo, + ServiceTradeNo: fmt.Sprintf("USR%d-%s", id, tradeNo), + Name: fmt.Sprintf("TUC%d", req.Amount), Money: strconv.FormatFloat(payMoney, 'f', 2, 64), Device: epay.PC, NotifyUrl: notifyUrl, diff --git a/controller/user.go b/controller/user.go index a6798eb..6faec2b 100644 --- a/controller/user.go +++ b/controller/user.go @@ -791,11 +791,11 @@ type topUpRequest struct { Key string `json:"key"` } -var lock = sync.Mutex{} +var topUpLock = sync.Mutex{} func TopUp(c *gin.Context) { - lock.Lock() - defer lock.Unlock() + topUpLock.Lock() + defer topUpLock.Unlock() req := topUpRequest{} err := c.ShouldBindJSON(&req) if err != nil { From 8a9ff36fbf2d78026f2afddff1f12f46e6e09796 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Sat, 3 Aug 2024 16:55:29 +0800 Subject: [PATCH 02/22] =?UTF-8?q?chore:=20=E4=BC=98=E5=8C=96relay=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/relay.go | 75 +++++++++++++++++++++++++-------------- middleware/distributor.go | 1 - 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/controller/relay.go b/controller/relay.go index 0c79015..66339f4 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -39,38 +39,28 @@ func relayHandler(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusCode func Relay(c *gin.Context) { relayMode := constant.Path2RelayMode(c.Request.URL.Path) - retryTimes := common.RetryTimes requestId := c.GetString(common.RequestIdKey) - channelId := c.GetInt("channel_id") - channelType := c.GetInt("channel_type") - channelName := c.GetString("channel_name") group := c.GetString("group") originalModel := c.GetString("original_model") - openaiErr := relayHandler(c, relayMode) - c.Set("use_channel", []string{fmt.Sprintf("%d", channelId)}) - if openaiErr != nil { - go processChannelError(c, channelId, channelType, channelName, openaiErr) - } else { - retryTimes = 0 - } - for i := 0; shouldRetry(c, channelId, openaiErr, retryTimes) && i < retryTimes; i++ { - channel, err := model.CacheGetRandomSatisfiedChannel(group, originalModel, i) + var openaiErr *dto.OpenAIErrorWithStatusCode + + for i := 0; i <= common.RetryTimes; i++ { + channel, err := getChannel(c, group, originalModel, i) if err != nil { - common.LogError(c.Request.Context(), fmt.Sprintf("CacheGetRandomSatisfiedChannel failed: %s", err.Error())) + common.LogError(c, fmt.Sprintf("Failed to get channel: %s", err.Error())) break } - channelId = channel.Id - useChannel := c.GetStringSlice("use_channel") - useChannel = append(useChannel, fmt.Sprintf("%d", channel.Id)) - c.Set("use_channel", useChannel) - common.LogInfo(c.Request.Context(), fmt.Sprintf("using channel #%d to retry (remain times %d)", channel.Id, i)) - middleware.SetupContextForSelectedChannel(c, channel, originalModel) - requestBody, err := common.GetRequestBody(c) - c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody)) - openaiErr = relayHandler(c, relayMode) - if openaiErr != nil { - go processChannelError(c, channel.Id, channel.Type, channel.Name, openaiErr) + openaiErr = relayRequest(c, relayMode, channel) + + if openaiErr == nil { + return // 成功处理请求,直接返回 + } + + go processChannelError(c, channel.Id, channel.Type, channel.Name, openaiErr) + + if !shouldRetry(c, openaiErr, common.RetryTimes-i) { + break } } useChannel := c.GetStringSlice("use_channel") @@ -90,7 +80,36 @@ func Relay(c *gin.Context) { } } -func shouldRetry(c *gin.Context, channelId int, openaiErr *dto.OpenAIErrorWithStatusCode, retryTimes int) bool { +func relayRequest(c *gin.Context, relayMode int, channel *model.Channel) *dto.OpenAIErrorWithStatusCode { + addUsedChannel(c, channel.Id) + requestBody, _ := common.GetRequestBody(c) + c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody)) + return relayHandler(c, relayMode) +} + +func addUsedChannel(c *gin.Context, channelId int) { + useChannel := c.GetStringSlice("use_channel") + useChannel = append(useChannel, fmt.Sprintf("%d", channelId)) + c.Set("use_channel", useChannel) +} + +func getChannel(c *gin.Context, group, originalModel string, retryCount int) (*model.Channel, error) { + if retryCount == 0 { + return &model.Channel{ + Id: c.GetInt("channel_id"), + Type: c.GetInt("channel_type"), + Name: c.GetString("channel_name"), + }, nil + } + channel, err := model.CacheGetRandomSatisfiedChannel(group, originalModel, retryCount) + if err != nil { + return nil, err + } + middleware.SetupContextForSelectedChannel(c, channel, originalModel) + return channel, nil +} + +func shouldRetry(c *gin.Context, openaiErr *dto.OpenAIErrorWithStatusCode, retryTimes int) bool { if openaiErr == nil { return false } @@ -114,6 +133,10 @@ func shouldRetry(c *gin.Context, channelId int, openaiErr *dto.OpenAIErrorWithSt return true } if openaiErr.StatusCode == http.StatusBadRequest { + channelType := c.GetInt("channel_type") + if channelType == common.ChannelTypeAnthropic { + return true + } return false } if openaiErr.StatusCode == 408 { diff --git a/middleware/distributor.go b/middleware/distributor.go index f150b41..fad868d 100644 --- a/middleware/distributor.go +++ b/middleware/distributor.go @@ -184,7 +184,6 @@ func SetupContextForSelectedChannel(c *gin.Context, channel *model.Channel, mode if channel == nil { return } - c.Set("channel", channel.Type) c.Set("channel_id", channel.Id) c.Set("channel_name", channel.Name) c.Set("channel_type", channel.Type) From fbe6cd75b1531311dc1438fb78a64ffe8b56e083 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Sat, 3 Aug 2024 17:07:14 +0800 Subject: [PATCH 03/22] =?UTF-8?q?chore:=20=E4=BC=98=E5=8C=96relay=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/relay.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/controller/relay.go b/controller/relay.go index 66339f4..4726d7e 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -47,8 +47,14 @@ func Relay(c *gin.Context) { for i := 0; i <= common.RetryTimes; i++ { channel, err := getChannel(c, group, originalModel, i) if err != nil { - common.LogError(c, fmt.Sprintf("Failed to get channel: %s", err.Error())) - break + errMsg := fmt.Sprintf("获取渠道出错: %s", err.Error()) + common.LogError(c, errMsg) + openaiErr = service.OpenAIErrorWrapperLocal(err, "get_channel_failed", http.StatusInternalServerError) + openaiErr.Error.Message = common.MessageWithRequestId(errMsg, requestId) + c.JSON(openaiErr.StatusCode, gin.H{ + "error": openaiErr.Error, + }) + return } openaiErr = relayRequest(c, relayMode, channel) From dd12a0052f1f9c8110230aa4b8567ea345a10fc4 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Sat, 3 Aug 2024 17:12:16 +0800 Subject: [PATCH 04/22] =?UTF-8?q?chore:=20=E4=BC=98=E5=8C=96relay=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/relay.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/controller/relay.go b/controller/relay.go index 4726d7e..30217f0 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -2,6 +2,7 @@ package controller import ( "bytes" + "errors" "fmt" "github.com/gin-gonic/gin" "io" @@ -47,14 +48,9 @@ func Relay(c *gin.Context) { for i := 0; i <= common.RetryTimes; i++ { channel, err := getChannel(c, group, originalModel, i) if err != nil { - errMsg := fmt.Sprintf("获取渠道出错: %s", err.Error()) - common.LogError(c, errMsg) + common.LogError(c, err.Error()) openaiErr = service.OpenAIErrorWrapperLocal(err, "get_channel_failed", http.StatusInternalServerError) - openaiErr.Error.Message = common.MessageWithRequestId(errMsg, requestId) - c.JSON(openaiErr.StatusCode, gin.H{ - "error": openaiErr.Error, - }) - return + break } openaiErr = relayRequest(c, relayMode, channel) @@ -72,7 +68,7 @@ func Relay(c *gin.Context) { useChannel := c.GetStringSlice("use_channel") if len(useChannel) > 1 { retryLogStr := fmt.Sprintf("重试:%s", strings.Trim(strings.Join(strings.Fields(fmt.Sprint(useChannel)), "->"), "[]")) - common.LogInfo(c.Request.Context(), retryLogStr) + common.LogInfo(c, retryLogStr) } if openaiErr != nil { @@ -109,7 +105,7 @@ func getChannel(c *gin.Context, group, originalModel string, retryCount int) (*m } channel, err := model.CacheGetRandomSatisfiedChannel(group, originalModel, retryCount) if err != nil { - return nil, err + return nil, errors.New(fmt.Sprintf("获取重试渠道失败: %s", err.Error())) } middleware.SetupContextForSelectedChannel(c, channel, originalModel) return channel, nil From afd328efcfcdfb44efa1f70c42f30a93c8fae1b9 Mon Sep 17 00:00:00 2001 From: HowieWu <98788152+utopeadia@users.noreply.github.com> Date: Sat, 3 Aug 2024 17:19:44 +0800 Subject: [PATCH 05/22] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 971871a..b561cda 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ - `GET_MEDIA_TOKEN`:是统计图片token,默认为 `true`,关闭后将不再在本地计算图片token,可能会导致和上游计费不同,此项覆盖 `GET_MEDIA_TOKEN_NOT_STREAM` 选项作用。 - `GET_MEDIA_TOKEN_NOT_STREAM`:是否在非流(`stream=false`)情况下统计图片token,默认为 `true`。 - `UPDATE_TASK`:是否更新异步任务(Midjourney、Suno),默认为 `true`,关闭后将不会更新任务进度。 -- `GEMINI_MODEL_MAP`:Gemini模型指定版本(v1/v1beta),使用模型:版本指定,","分隔,例如:-e GEMINI_MODEL_API="gemini-1.5-pro-latest:v1beta,gemini-1.5-pro-001:v1beta",为空则使用默认配置 +- `GEMINI_MODEL_MAP`:Gemini模型指定版本(v1/v1beta),使用“模型:版本”指定,","分隔,例如:-e GEMINI_MODEL_MAP="gemini-1.5-pro-latest:v1beta,gemini-1.5-pro-001:v1beta",为空则使用默认配置 ## 部署 ### 部署要求 From 5acf0745412caf80b05d97477e080df62cbf0a89 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Sat, 3 Aug 2024 17:32:28 +0800 Subject: [PATCH 06/22] =?UTF-8?q?chore:=20=E4=BC=98=E5=8C=96=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E7=A6=81=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/channel-test.go | 2 +- controller/relay.go | 19 +++++++++++++------ middleware/distributor.go | 7 +------ model/channel.go | 7 +++++++ relay/relay-mj.go | 2 +- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/controller/channel-test.go b/controller/channel-test.go index fe27978..95c4a60 100644 --- a/controller/channel-test.go +++ b/controller/channel-test.go @@ -240,7 +240,7 @@ func testAllChannels(notify bool) error { } // parse *int to bool - if channel.AutoBan != nil && *channel.AutoBan == 0 { + if !channel.GetAutoBan() { ban = false } diff --git a/controller/relay.go b/controller/relay.go index 30217f0..3a6fb6f 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -59,7 +59,7 @@ func Relay(c *gin.Context) { return // 成功处理请求,直接返回 } - go processChannelError(c, channel.Id, channel.Type, channel.Name, openaiErr) + go processChannelError(c, channel.Id, channel.Type, channel.Name, channel.GetAutoBan(), openaiErr) if !shouldRetry(c, openaiErr, common.RetryTimes-i) { break @@ -97,10 +97,16 @@ func addUsedChannel(c *gin.Context, channelId int) { func getChannel(c *gin.Context, group, originalModel string, retryCount int) (*model.Channel, error) { if retryCount == 0 { + autoBan := c.GetBool("auto_ban") + autoBanInt := 1 + if !autoBan { + autoBanInt = 0 + } return &model.Channel{ - Id: c.GetInt("channel_id"), - Type: c.GetInt("channel_type"), - Name: c.GetString("channel_name"), + Id: c.GetInt("channel_id"), + Type: c.GetInt("channel_type"), + Name: c.GetString("channel_name"), + AutoBan: &autoBanInt, }, nil } channel, err := model.CacheGetRandomSatisfiedChannel(group, originalModel, retryCount) @@ -154,8 +160,9 @@ func shouldRetry(c *gin.Context, openaiErr *dto.OpenAIErrorWithStatusCode, retry return true } -func processChannelError(c *gin.Context, channelId int, channelType int, channelName string, err *dto.OpenAIErrorWithStatusCode) { - autoBan := c.GetBool("auto_ban") +func processChannelError(c *gin.Context, channelId int, channelType int, channelName string, autoBan bool, err *dto.OpenAIErrorWithStatusCode) { + // 不要使用context获取渠道信息,异步处理时可能会出现渠道信息不一致的情况 + // do not use context to get channel info, there may be inconsistent channel info when processing asynchronously common.LogError(c.Request.Context(), fmt.Sprintf("relay error (channel #%d, status code: %d): %s", channelId, err.StatusCode, err.Error.Message)) if service.ShouldDisableChannel(channelType, err) && autoBan { service.DisableChannel(channelId, channelName, err.Error.Message) diff --git a/middleware/distributor.go b/middleware/distributor.go index fad868d..1be3b31 100644 --- a/middleware/distributor.go +++ b/middleware/distributor.go @@ -187,15 +187,10 @@ func SetupContextForSelectedChannel(c *gin.Context, channel *model.Channel, mode c.Set("channel_id", channel.Id) c.Set("channel_name", channel.Name) c.Set("channel_type", channel.Type) - ban := true - // parse *int to bool - if channel.AutoBan != nil && *channel.AutoBan == 0 { - ban = false - } if nil != channel.OpenAIOrganization && "" != *channel.OpenAIOrganization { c.Set("channel_organization", *channel.OpenAIOrganization) } - c.Set("auto_ban", ban) + c.Set("auto_ban", channel.GetAutoBan()) c.Set("model_mapping", channel.GetModelMapping()) c.Set("status_code_mapping", channel.GetStatusCodeMapping()) c.Request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", channel.Key)) diff --git a/model/channel.go b/model/channel.go index 7db3f07..3f9d9ed 100644 --- a/model/channel.go +++ b/model/channel.go @@ -61,6 +61,13 @@ func (channel *Channel) SetOtherInfo(otherInfo map[string]interface{}) { channel.OtherInfo = string(otherInfoBytes) } +func (channel *Channel) GetAutoBan() bool { + if channel.AutoBan == nil { + return false + } + return *channel.AutoBan == 1 +} + func (channel *Channel) Save() error { return DB.Save(channel).Error } diff --git a/relay/relay-mj.go b/relay/relay-mj.go index 73ea468..4dd81c5 100644 --- a/relay/relay-mj.go +++ b/relay/relay-mj.go @@ -549,7 +549,7 @@ func RelayMidjourneySubmit(c *gin.Context, relayMode int) *dto.MidjourneyRespons if err != nil { common.SysError("get_channel_null: " + err.Error()) } - if channel.AutoBan != nil && *channel.AutoBan == 1 && common.AutomaticDisableChannelEnabled { + if channel.GetAutoBan() && common.AutomaticDisableChannelEnabled { model.UpdateChannelStatusById(midjourneyTask.ChannelId, 2, "No available account instance") } } From 0123ad4d61986f8d4c73b034d030dc01d43bc9e5 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Sat, 3 Aug 2024 17:46:13 +0800 Subject: [PATCH 07/22] =?UTF-8?q?fix:=20=E9=87=8D=E8=AF=95=E5=90=8Erequest?= =?UTF-8?q?=20id=E4=B8=8D=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/relay.go | 8 ++++---- relay/relay-text.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/controller/relay.go b/controller/relay.go index 3a6fb6f..13fbde0 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -163,7 +163,7 @@ func shouldRetry(c *gin.Context, openaiErr *dto.OpenAIErrorWithStatusCode, retry func processChannelError(c *gin.Context, channelId int, channelType int, channelName string, autoBan bool, err *dto.OpenAIErrorWithStatusCode) { // 不要使用context获取渠道信息,异步处理时可能会出现渠道信息不一致的情况 // do not use context to get channel info, there may be inconsistent channel info when processing asynchronously - common.LogError(c.Request.Context(), fmt.Sprintf("relay error (channel #%d, status code: %d): %s", channelId, err.StatusCode, err.Error.Message)) + common.LogError(c, fmt.Sprintf("relay error (channel #%d, status code: %d): %s", channelId, err.StatusCode, err.Error.Message)) if service.ShouldDisableChannel(channelType, err) && autoBan { service.DisableChannel(channelId, channelName, err.Error.Message) } @@ -240,14 +240,14 @@ func RelayTask(c *gin.Context) { for i := 0; shouldRetryTaskRelay(c, channelId, taskErr, retryTimes) && i < retryTimes; i++ { channel, err := model.CacheGetRandomSatisfiedChannel(group, originalModel, i) if err != nil { - common.LogError(c.Request.Context(), fmt.Sprintf("CacheGetRandomSatisfiedChannel failed: %s", err.Error())) + common.LogError(c, fmt.Sprintf("CacheGetRandomSatisfiedChannel failed: %s", err.Error())) break } channelId = channel.Id useChannel := c.GetStringSlice("use_channel") useChannel = append(useChannel, fmt.Sprintf("%d", channelId)) c.Set("use_channel", useChannel) - common.LogInfo(c.Request.Context(), fmt.Sprintf("using channel #%d to retry (remain times %d)", channel.Id, i)) + common.LogInfo(c, fmt.Sprintf("using channel #%d to retry (remain times %d)", channel.Id, i)) middleware.SetupContextForSelectedChannel(c, channel, originalModel) requestBody, err := common.GetRequestBody(c) @@ -257,7 +257,7 @@ func RelayTask(c *gin.Context) { useChannel := c.GetStringSlice("use_channel") if len(useChannel) > 1 { retryLogStr := fmt.Sprintf("重试:%s", strings.Trim(strings.Join(strings.Fields(fmt.Sprint(useChannel)), "->"), "[]")) - common.LogInfo(c.Request.Context(), retryLogStr) + common.LogInfo(c, retryLogStr) } if taskErr != nil { if taskErr.StatusCode == http.StatusTooManyRequests { diff --git a/relay/relay-text.go b/relay/relay-text.go index 636be56..e7c5388 100644 --- a/relay/relay-text.go +++ b/relay/relay-text.go @@ -253,13 +253,13 @@ func preConsumeQuota(c *gin.Context, preConsumedQuota int, relayInfo *relaycommo if tokenQuota > 100*preConsumedQuota { // 令牌额度充足,信任令牌 preConsumedQuota = 0 - common.LogInfo(c.Request.Context(), fmt.Sprintf("user %d quota %d and token %d quota %d are enough, trusted and no need to pre-consume", relayInfo.UserId, userQuota, relayInfo.TokenId, tokenQuota)) + common.LogInfo(c, fmt.Sprintf("user %d quota %d and token %d quota %d are enough, trusted and no need to pre-consume", relayInfo.UserId, userQuota, relayInfo.TokenId, tokenQuota)) } } else { // in this case, we do not pre-consume quota // because the user has enough quota preConsumedQuota = 0 - common.LogInfo(c.Request.Context(), fmt.Sprintf("user %d with unlimited token has enough quota %d, trusted and no need to pre-consume", relayInfo.UserId, userQuota)) + common.LogInfo(c, fmt.Sprintf("user %d with unlimited token has enough quota %d, trusted and no need to pre-consume", relayInfo.UserId, userQuota)) } } if preConsumedQuota > 0 { From 0b4ef42d861380d45ddae569bcb4c8248e92b30b Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Sat, 3 Aug 2024 22:41:47 +0800 Subject: [PATCH 08/22] fix: channel typ error --- relay/common/relay_info.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index 564a7ad..3ed5ee3 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -33,7 +33,7 @@ type RelayInfo struct { } func GenRelayInfo(c *gin.Context) *RelayInfo { - channelType := c.GetInt("channel") + channelType := c.GetInt("channel_type") channelId := c.GetInt("channel_id") tokenId := c.GetInt("token_id") @@ -112,7 +112,7 @@ type TaskRelayInfo struct { } func GenTaskRelayInfo(c *gin.Context) *TaskRelayInfo { - channelType := c.GetInt("channel") + channelType := c.GetInt("channel_type") channelId := c.GetInt("channel_id") tokenId := c.GetInt("token_id") From 5d0d268c975a68108c3ba1256c29c3dd289ac25d Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Sun, 4 Aug 2024 00:17:48 +0800 Subject: [PATCH 09/22] fix: epay --- controller/topup.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/controller/topup.go b/controller/topup.go index dc1e265..995f412 100644 --- a/controller/topup.go +++ b/controller/topup.go @@ -94,6 +94,7 @@ func RequestEpay(c *gin.Context) { returnUrl, _ := url.Parse(constant.ServerAddress + "/log") notifyUrl, _ := url.Parse(callBackAddress + "/api/user/epay/notify") tradeNo := fmt.Sprintf("%s%d", common.GetRandomString(6), time.Now().Unix()) + tradeNo = fmt.Sprintf("USR%dNO%s", id, tradeNo) client := GetEpayClient() if client == nil { c.JSON(200, gin.H{"message": "error", "data": "当前管理员未配置支付信息"}) @@ -101,7 +102,7 @@ func RequestEpay(c *gin.Context) { } uri, params, err := client.Purchase(&epay.PurchaseArgs{ Type: payType, - ServiceTradeNo: fmt.Sprintf("USR%d-%s", id, tradeNo), + ServiceTradeNo: tradeNo, Name: fmt.Sprintf("TUC%d", req.Amount), Money: strconv.FormatFloat(payMoney, 'f', 2, 64), Device: epay.PC, @@ -120,7 +121,7 @@ func RequestEpay(c *gin.Context) { UserId: id, Amount: amount, Money: payMoney, - TradeNo: "A" + tradeNo, + TradeNo: tradeNo, CreateTime: time.Now().Unix(), Status: "pending", } From a0a3807bd4e6ea7a43161efe622e059f74d5d09c Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Sun, 4 Aug 2024 03:12:24 +0800 Subject: [PATCH 10/22] chore: epay --- controller/topup.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/controller/topup.go b/controller/topup.go index 995f412..c4b1aa9 100644 --- a/controller/topup.go +++ b/controller/topup.go @@ -41,12 +41,12 @@ func GetEpayClient() *epay.Client { return withUrl } -func getPayMoney(amount float64, user model.User) float64 { +func getPayMoney(amount float64, group string) float64 { if !common.DisplayInCurrencyEnabled { amount = amount / common.QuotaPerUnit } // 别问为什么用float64,问就是这么点钱没必要 - topupGroupRatio := common.GetTopupGroupRatio(user.Group) + topupGroupRatio := common.GetTopupGroupRatio(group) if topupGroupRatio == 0 { topupGroupRatio = 1 } @@ -75,8 +75,12 @@ func RequestEpay(c *gin.Context) { } id := c.GetInt("id") - user, _ := model.GetUserById(id, false) - payMoney := getPayMoney(float64(req.Amount), *user) + group, err := model.CacheGetUserGroup(id) + if err != nil { + c.JSON(200, gin.H{"message": "error", "data": "获取用户分组失败"}) + return + } + payMoney := getPayMoney(float64(req.Amount), group) if payMoney < 0.01 { c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"}) return @@ -233,8 +237,12 @@ func RequestAmount(c *gin.Context) { return } id := c.GetInt("id") - user, _ := model.GetUserById(id, false) - payMoney := getPayMoney(float64(req.Amount), *user) + group, err := model.CacheGetUserGroup(id) + if err != nil { + c.JSON(200, gin.H{"message": "error", "data": "获取用户分组失败"}) + return + } + payMoney := getPayMoney(float64(req.Amount), group) if payMoney <= 0.01 { c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"}) return From 67878731fc198e47bbf62fe6cbd06262d94c10d3 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Sun, 4 Aug 2024 14:35:16 +0800 Subject: [PATCH 11/22] feat: log user id --- middleware/auth.go | 6 ++++++ model/token.go | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/middleware/auth.go b/middleware/auth.go index edd15de..d2c9b3c 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -143,6 +143,12 @@ func TokenAuth() func(c *gin.Context) { key = parts[0] } token, err := model.ValidateUserToken(key) + if token != nil { + id := c.GetInt("id") + if id == 0 { + c.Set("id", token.Id) + } + } if err != nil { abortWithOpenAiMessage(c, http.StatusUnauthorized, err.Error()) return diff --git a/model/token.go b/model/token.go index 27907af..272c573 100644 --- a/model/token.go +++ b/model/token.go @@ -51,12 +51,12 @@ func ValidateUserToken(key string) (token *Token, err error) { if token.Status == common.TokenStatusExhausted { keyPrefix := key[:3] keySuffix := key[len(key)-3:] - return nil, errors.New("该令牌额度已用尽 TokenStatusExhausted[sk-" + keyPrefix + "***" + keySuffix + "]") + return token, errors.New("该令牌额度已用尽 TokenStatusExhausted[sk-" + keyPrefix + "***" + keySuffix + "]") } else if token.Status == common.TokenStatusExpired { - return nil, errors.New("该令牌已过期") + return token, errors.New("该令牌已过期") } if token.Status != common.TokenStatusEnabled { - return nil, errors.New("该令牌状态不可用") + return token, errors.New("该令牌状态不可用") } if token.ExpiredTime != -1 && token.ExpiredTime < common.GetTimestamp() { if !common.RedisEnabled { @@ -66,7 +66,7 @@ func ValidateUserToken(key string) (token *Token, err error) { common.SysError("failed to update token status" + err.Error()) } } - return nil, errors.New("该令牌已过期") + return token, errors.New("该令牌已过期") } if !token.UnlimitedQuota && token.RemainQuota <= 0 { if !common.RedisEnabled { @@ -79,7 +79,7 @@ func ValidateUserToken(key string) (token *Token, err error) { } keyPrefix := key[:3] keySuffix := key[len(key)-3:] - return nil, errors.New(fmt.Sprintf("[sk-%s***%s] 该令牌额度已用尽 !token.UnlimitedQuota && token.RemainQuota = %d", keyPrefix, keySuffix, token.RemainQuota)) + return token, errors.New(fmt.Sprintf("[sk-%s***%s] 该令牌额度已用尽 !token.UnlimitedQuota && token.RemainQuota = %d", keyPrefix, keySuffix, token.RemainQuota)) } return token, nil } From 190316f66e9c34d37866cb3db8881725f0e4bc5c Mon Sep 17 00:00:00 2001 From: FENG Date: Mon, 5 Aug 2024 22:35:16 +0800 Subject: [PATCH 12/22] =?UTF-8?q?fix:=20=E6=B8=A0=E9=81=93=E5=A4=9A?= =?UTF-8?q?=E5=88=86=E7=BB=84=EF=BC=8C=E4=BC=98=E5=8C=96=E5=88=86=E7=BB=84?= =?UTF-8?q?=20like=20=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/channel.go | 8 +- web/src/components/ChannelsTable.js | 801 ++++++++++++++-------------- 2 files changed, 404 insertions(+), 405 deletions(-) diff --git a/model/channel.go b/model/channel.go index 3f9d9ed..8000537 100644 --- a/model/channel.go +++ b/model/channel.go @@ -90,13 +90,11 @@ func GetAllChannels(startIdx int, num int, selectAll bool, idSort bool) ([]*Chan func SearchChannels(keyword string, group string, model string) ([]*Channel, error) { var channels []*Channel keyCol := "`key`" - groupCol := "`group`" modelsCol := "`models`" // 如果是 PostgreSQL,使用双引号 if common.UsingPostgreSQL { keyCol = `"key"` - groupCol = `"group"` modelsCol = `"models"` } @@ -106,9 +104,9 @@ func SearchChannels(keyword string, group string, model string) ([]*Channel, err // 构造WHERE子句 var whereClause string var args []interface{} - if group != "" { - whereClause = "(id = ? OR name LIKE ? OR " + keyCol + " = ?) AND " + groupCol + " = ? AND " + modelsCol + " LIKE ?" - args = append(args, common.String2Int(keyword), "%"+keyword+"%", keyword, group, "%"+model+"%") + if group != "" && group != "null" { + whereClause = "(id = ? OR name LIKE ? OR " + keyCol + " = ?) AND " + modelsCol + ` LIKE ? AND (',' || "group" || ',') LIKE ?` + args = append(args, common.String2Int(keyword), "%"+keyword+"%", keyword, "%"+model+"%", "%,"+group+",%") } else { whereClause = "(id = ? OR name LIKE ? OR " + keyCol + " = ?) AND " + modelsCol + " LIKE ?" args = append(args, common.String2Int(keyword), "%"+keyword+"%", keyword, "%"+model+"%") diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js index f2c7518..ea4e22a 100644 --- a/web/src/components/ChannelsTable.js +++ b/web/src/components/ChannelsTable.js @@ -49,9 +49,9 @@ function renderType(type) { type2label[0] = { value: 0, text: '未知类型', color: 'grey' }; } return ( - - {type2label[type]?.text} - + + {type2label[type]?.text} + ); } @@ -75,13 +75,13 @@ const ChannelsTable = () => { dataIndex: 'group', render: (text, record, index) => { return ( -
- - {text.split(',').map((item, index) => { - return renderGroup(item); - })} - -
+
+ + {text.split(',').map((item, index) => { + return renderGroup(item); + })} + +
); }, }, @@ -104,11 +104,11 @@ const ChannelsTable = () => { let reason = otherInfo['status_reason']; let time = otherInfo['status_time']; return ( -
- - {renderStatus(text)} - -
+
+ + {renderStatus(text)} + +
); } else { return renderStatus(text); @@ -127,27 +127,27 @@ const ChannelsTable = () => { dataIndex: 'expired_time', render: (text, record, index) => { return ( -
- - - - {renderQuota(record.used_quota)} - - - - { - updateChannelBalance(record); - }} - > - ${renderNumberWithPoint(record.balance)} - - - -
+
+ + + + {renderQuota(record.used_quota)} + + + + { + updateChannelBalance(record); + }} + > + ${renderNumberWithPoint(record.balance)} + + + +
); }, }, @@ -156,19 +156,19 @@ const ChannelsTable = () => { dataIndex: 'priority', render: (text, record, index) => { return ( -
- { - manageChannel(record.id, 'priority', record, e.target.value); - }} - keepFocus={true} - innerButtons - defaultValue={record.priority} - min={-999} - /> -
+
+ { + manageChannel(record.id, 'priority', record, e.target.value); + }} + keepFocus={true} + innerButtons + defaultValue={record.priority} + min={-999} + /> +
); }, }, @@ -177,19 +177,19 @@ const ChannelsTable = () => { dataIndex: 'weight', render: (text, record, index) => { return ( -
- { - manageChannel(record.id, 'weight', record, e.target.value); - }} - keepFocus={true} - innerButtons - defaultValue={record.weight} - min={0} - /> -
+
+ { + manageChannel(record.id, 'weight', record, e.target.value); + }} + keepFocus={true} + innerButtons + defaultValue={record.weight} + min={0} + /> +
); }, }, @@ -197,95 +197,95 @@ const ChannelsTable = () => { title: '', dataIndex: 'operate', render: (text, record, index) => ( -
- - - + - - - {/**/} - { - manageChannel(record.id, 'delete', record).then(() => { - removeRecord(record.id); - }); - }} - > - - - {record.status === 1 ? ( - + + + + + {/**/} + { + manageChannel(record.id, 'delete', record).then(() => { + removeRecord(record.id); + }); + }} > - 禁用 - - ) : ( + + + {record.status === 1 ? ( + + ) : ( + + )} - )} - - { - copySelectedChannel(record.id); - }} - > - - -
+ { + copySelectedChannel(record.id); + }} + > + + + ), }, ]; @@ -301,7 +301,7 @@ const ChannelsTable = () => { const [updatingBalance, setUpdatingBalance] = useState(false); const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE); const [showPrompt, setShowPrompt] = useState( - shouldShowPrompt('channel-test'), + shouldShowPrompt('channel-test'), ); const [channelCount, setChannelCount] = useState(pageSize); const [groupOptions, setGroupOptions] = useState([]); @@ -357,7 +357,7 @@ const ChannelsTable = () => { const loadChannels = async (startIdx, pageSize, idSort) => { setLoading(true); const res = await API.get( - `/api/channel/?p=${startIdx}&page_size=${pageSize}&id_sort=${idSort}`, + `/api/channel/?p=${startIdx}&page_size=${pageSize}&id_sort=${idSort}`, ); if (res === undefined) { return; @@ -379,7 +379,7 @@ const ChannelsTable = () => { const copySelectedChannel = async (id) => { const channelToCopy = channels.find( - (channel) => String(channel.id) === String(id), + (channel) => String(channel.id) === String(id), ); console.log(channelToCopy); channelToCopy.name += '_复制'; @@ -412,14 +412,14 @@ const ChannelsTable = () => { // console.log('default effect') const localIdSort = localStorage.getItem('id-sort') === 'true'; const localPageSize = - parseInt(localStorage.getItem('page-size')) || ITEMS_PER_PAGE; + parseInt(localStorage.getItem('page-size')) || ITEMS_PER_PAGE; setIdSort(localIdSort); setPageSize(localPageSize); loadChannels(0, localPageSize, localIdSort) - .then() - .catch((reason) => { - showError(reason); - }); + .then() + .catch((reason) => { + showError(reason); + }); fetchGroups().then(); loadChannelModels().then(); }, []); @@ -476,27 +476,27 @@ const ChannelsTable = () => { switch (status) { case 1: return ( - - 已启用 - + + 已启用 + ); case 2: return ( - - 已禁用 - + + 已禁用 + ); case 3: return ( - - 自动禁用 - + + 自动禁用 + ); default: return ( - - 未知状态 - + + 未知状态 + ); } }; @@ -506,33 +506,33 @@ const ChannelsTable = () => { time = time.toFixed(2) + ' 秒'; if (responseTime === 0) { return ( - - 未测试 - + + 未测试 + ); } else if (responseTime <= 1000) { return ( - - {time} - + + {time} + ); } else if (responseTime <= 3000) { return ( - - {time} - + + {time} + ); } else if (responseTime <= 5000) { return ( - - {time} - + + {time} + ); } else { return ( - - {time} - + + {time} + ); } }; @@ -546,7 +546,7 @@ const ChannelsTable = () => { } setSearching(true); const res = await API.get( - `/api/channel/search?keyword=${searchKeyword}&group=${searchGroup}&model=${searchModel}`, + `/api/channel/search?keyword=${searchKeyword}&group=${searchGroup}&model=${searchModel}`, ); const { success, message, data } = res.data; if (success) { @@ -648,8 +648,8 @@ const ChannelsTable = () => { }; let pageData = channels.slice( - (activePage - 1) * pageSize, - activePage * pageSize, + (activePage - 1) * pageSize, + activePage * pageSize, ); const handlePageChange = (page) => { @@ -665,10 +665,10 @@ const ChannelsTable = () => { setPageSize(size); setActivePage(1); loadChannels(0, size, idSort) - .then() - .catch((reason) => { - showError(reason); - }); + .then() + .catch((reason) => { + showError(reason); + }); }; const fetchGroups = async () => { @@ -680,10 +680,10 @@ const ChannelsTable = () => { return; } setGroupOptions( - res.data.data.map((group) => ({ - label: group, - value: group, - })), + res.data.data.map((group) => ({ + label: group, + value: group, + })), ); } catch (error) { showError(error.message); @@ -707,225 +707,226 @@ const ChannelsTable = () => { }; return ( - <> - -
{ - searchChannels(searchKeyword, searchGroup, searchModel); - }} - labelPosition='left' - > -
+ <> + + { + searchChannels(searchKeyword, searchGroup, searchModel); + }} + labelPosition='left' + > +
+ + { + setSearchKeyword(v.trim()); + }} + /> + { + setSearchModel(v.trim()); + }} + /> + { + setSearchGroup(v); + searchChannels(searchKeyword, v, searchModel); + }} + /> + + +
+ +
- { - setSearchKeyword(v.trim()); - }} - /> - { - setSearchModel(v.trim()); - }} - /> - { - setSearchGroup(v); - searchChannels(searchKeyword, v, searchModel); - }} - /> - + + 使用ID排序 + { + localStorage.setItem('id-sort', v + ''); + setIdSort(v); + loadChannels(0, pageSize, v) + .then() + .catch((reason) => { + showError(reason); + }); + }} + > +
- -
- - - 使用ID排序 - { - localStorage.setItem('id-sort', v + ''); - setIdSort(v); - loadChannels(0, pageSize, v) - .then() - .catch((reason) => { - showError(reason); - }); - }} - > - - -
- '', - onPageSizeChange: (size) => { - handlePageSizeChange(size).then(); - }, - onPageChange: handlePageChange, - }} - loading={loading} - onRow={handleRow} - rowSelection={ - enableBatchDelete - ? { - onChange: (selectedRowKeys, selectedRows) => { - // console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); - setSelectedChannels(selectedRows); - }, - } - : null - } - /> -
- '', + onPageSizeChange: (size) => { + handlePageSizeChange(size).then(); + }, + onPageChange: handlePageChange, + }} + loading={loading} + onRow={handleRow} + rowSelection={ + enableBatchDelete + ? { + onChange: (selectedRowKeys, selectedRows) => { + // console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); + setSelectedChannels(selectedRows); + }, + } + : null + } + /> +
- - - - - - - - - - - - - - {/*
*/} - - {/*
*/} -
-
- - 开启批量删除 - { - setEnableBatchDelete(v); - }} - > - - - - + + + + + + + + + - - -
- +
+ {/*
*/} + + {/*
*/} +
+
+ + 开启批量删除 + { + setEnableBatchDelete(v); + }} + > + + + + + + + +
+ ); }; From c152b4de08f4747ff5ee69aed3bab4c8f8f86033 Mon Sep 17 00:00:00 2001 From: FENG Date: Tue, 6 Aug 2024 15:40:44 +0800 Subject: [PATCH 13/22] chore: indent recovery --- web/src/components/ChannelsTable.js | 806 ++++++++++++++-------------- 1 file changed, 403 insertions(+), 403 deletions(-) diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js index ea4e22a..c98f73f 100644 --- a/web/src/components/ChannelsTable.js +++ b/web/src/components/ChannelsTable.js @@ -49,9 +49,9 @@ function renderType(type) { type2label[0] = { value: 0, text: '未知类型', color: 'grey' }; } return ( - - {type2label[type]?.text} - + + {type2label[type]?.text} + ); } @@ -75,13 +75,13 @@ const ChannelsTable = () => { dataIndex: 'group', render: (text, record, index) => { return ( -
- - {text.split(',').map((item, index) => { - return renderGroup(item); - })} - -
+
+ + {text.split(',').map((item, index) => { + return renderGroup(item); + })} + +
); }, }, @@ -104,11 +104,11 @@ const ChannelsTable = () => { let reason = otherInfo['status_reason']; let time = otherInfo['status_time']; return ( -
- - {renderStatus(text)} - -
+
+ + {renderStatus(text)} + +
); } else { return renderStatus(text); @@ -127,27 +127,27 @@ const ChannelsTable = () => { dataIndex: 'expired_time', render: (text, record, index) => { return ( -
- - - - {renderQuota(record.used_quota)} - - - - { - updateChannelBalance(record); - }} - > - ${renderNumberWithPoint(record.balance)} - - - -
+
+ + + + {renderQuota(record.used_quota)} + + + + { + updateChannelBalance(record); + }} + > + ${renderNumberWithPoint(record.balance)} + + + +
); }, }, @@ -156,19 +156,19 @@ const ChannelsTable = () => { dataIndex: 'priority', render: (text, record, index) => { return ( -
- { - manageChannel(record.id, 'priority', record, e.target.value); - }} - keepFocus={true} - innerButtons - defaultValue={record.priority} - min={-999} - /> -
+
+ { + manageChannel(record.id, 'priority', record, e.target.value); + }} + keepFocus={true} + innerButtons + defaultValue={record.priority} + min={-999} + /> +
); }, }, @@ -177,19 +177,19 @@ const ChannelsTable = () => { dataIndex: 'weight', render: (text, record, index) => { return ( -
- { - manageChannel(record.id, 'weight', record, e.target.value); - }} - keepFocus={true} - innerButtons - defaultValue={record.weight} - min={0} - /> -
+
+ { + manageChannel(record.id, 'weight', record, e.target.value); + }} + keepFocus={true} + innerButtons + defaultValue={record.weight} + min={0} + /> +
); }, }, @@ -197,95 +197,95 @@ const ChannelsTable = () => { title: '', dataIndex: 'operate', render: (text, record, index) => ( -
- + + + - - - - - {/**/} - { - manageChannel(record.id, 'delete', record).then(() => { - removeRecord(record.id); - }); - }} - > - - - {record.status === 1 ? ( - - ) : ( - - )} - + + + {/**/} + { + manageChannel(record.id, 'delete', record).then(() => { + removeRecord(record.id); + }); + }} + > + - { - copySelectedChannel(record.id); - }} + + {record.status === 1 ? ( + - -
+ 禁用 + + ) : ( + + )} + + { + copySelectedChannel(record.id); + }} + > + + + ), }, ]; @@ -301,7 +301,7 @@ const ChannelsTable = () => { const [updatingBalance, setUpdatingBalance] = useState(false); const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE); const [showPrompt, setShowPrompt] = useState( - shouldShowPrompt('channel-test'), + shouldShowPrompt('channel-test'), ); const [channelCount, setChannelCount] = useState(pageSize); const [groupOptions, setGroupOptions] = useState([]); @@ -357,7 +357,7 @@ const ChannelsTable = () => { const loadChannels = async (startIdx, pageSize, idSort) => { setLoading(true); const res = await API.get( - `/api/channel/?p=${startIdx}&page_size=${pageSize}&id_sort=${idSort}`, + `/api/channel/?p=${startIdx}&page_size=${pageSize}&id_sort=${idSort}`, ); if (res === undefined) { return; @@ -379,7 +379,7 @@ const ChannelsTable = () => { const copySelectedChannel = async (id) => { const channelToCopy = channels.find( - (channel) => String(channel.id) === String(id), + (channel) => String(channel.id) === String(id), ); console.log(channelToCopy); channelToCopy.name += '_复制'; @@ -412,14 +412,14 @@ const ChannelsTable = () => { // console.log('default effect') const localIdSort = localStorage.getItem('id-sort') === 'true'; const localPageSize = - parseInt(localStorage.getItem('page-size')) || ITEMS_PER_PAGE; + parseInt(localStorage.getItem('page-size')) || ITEMS_PER_PAGE; setIdSort(localIdSort); setPageSize(localPageSize); loadChannels(0, localPageSize, localIdSort) - .then() - .catch((reason) => { - showError(reason); - }); + .then() + .catch((reason) => { + showError(reason); + }); fetchGroups().then(); loadChannelModels().then(); }, []); @@ -476,27 +476,27 @@ const ChannelsTable = () => { switch (status) { case 1: return ( - - 已启用 - + + 已启用 + ); case 2: return ( - - 已禁用 - + + 已禁用 + ); case 3: return ( - - 自动禁用 - + + 自动禁用 + ); default: return ( - - 未知状态 - + + 未知状态 + ); } }; @@ -506,33 +506,33 @@ const ChannelsTable = () => { time = time.toFixed(2) + ' 秒'; if (responseTime === 0) { return ( - - 未测试 - + + 未测试 + ); } else if (responseTime <= 1000) { return ( - - {time} - + + {time} + ); } else if (responseTime <= 3000) { return ( - - {time} - + + {time} + ); } else if (responseTime <= 5000) { return ( - - {time} - + + {time} + ); } else { return ( - - {time} - + + {time} + ); } }; @@ -546,7 +546,7 @@ const ChannelsTable = () => { } setSearching(true); const res = await API.get( - `/api/channel/search?keyword=${searchKeyword}&group=${searchGroup}&model=${searchModel}`, + `/api/channel/search?keyword=${searchKeyword}&group=${searchGroup}&model=${searchModel}`, ); const { success, message, data } = res.data; if (success) { @@ -648,8 +648,8 @@ const ChannelsTable = () => { }; let pageData = channels.slice( - (activePage - 1) * pageSize, - activePage * pageSize, + (activePage - 1) * pageSize, + activePage * pageSize, ); const handlePageChange = (page) => { @@ -665,10 +665,10 @@ const ChannelsTable = () => { setPageSize(size); setActivePage(1); loadChannels(0, size, idSort) - .then() - .catch((reason) => { - showError(reason); - }); + .then() + .catch((reason) => { + showError(reason); + }); }; const fetchGroups = async () => { @@ -680,10 +680,10 @@ const ChannelsTable = () => { return; } setGroupOptions( - res.data.data.map((group) => ({ - label: group, - value: group, - })), + res.data.data.map((group) => ({ + label: group, + value: group, + })), ); } catch (error) { showError(error.message); @@ -707,226 +707,226 @@ const ChannelsTable = () => { }; return ( - <> - -
{ - searchChannels(searchKeyword, searchGroup, searchModel); - }} - labelPosition='left' - > -
- - { - setSearchKeyword(v.trim()); - }} - /> - { - setSearchModel(v.trim()); - }} - /> - { - setSearchGroup(v); - searchChannels(searchKeyword, v, searchModel); - }} - /> - - -
- -
+ <> + +
{ + searchChannels(searchKeyword, searchGroup, searchModel); + }} + labelPosition='left' + > +
- - 使用ID排序 - { - localStorage.setItem('id-sort', v + ''); - setIdSort(v); - loadChannels(0, pageSize, v) - .then() - .catch((reason) => { - showError(reason); - }); - }} - > - + { + setSearchKeyword(v.trim()); + }} + /> + { + setSearchModel(v.trim()); + }} + /> + { + setSearchGroup(v); + searchChannels(searchKeyword, v, searchModel); + }} + /> +
+ +
+ + + 使用ID排序 + { + localStorage.setItem('id-sort', v + ''); + setIdSort(v); + loadChannels(0, pageSize, v) + .then() + .catch((reason) => { + showError(reason); + }); + }} + > + + +
-
'', - onPageSizeChange: (size) => { - handlePageSizeChange(size).then(); - }, - onPageChange: handlePageChange, - }} - loading={loading} - onRow={handleRow} - rowSelection={ - enableBatchDelete - ? { - onChange: (selectedRowKeys, selectedRows) => { - // console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); - setSelectedChannels(selectedRows); - }, - } - : null - } - /> -
'', + onPageSizeChange: (size) => { + handlePageSizeChange(size).then(); + }, + onPageChange: handlePageChange, + }} + loading={loading} + onRow={handleRow} + rowSelection={ + enableBatchDelete + ? { + onChange: (selectedRowKeys, selectedRows) => { + // console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); + setSelectedChannels(selectedRows); + }, + } + : null + } + /> +
+ - { + setEditingChannel({ + id: undefined, + }); + setShowEdit(true); + }} + > + 添加渠道 + + + + + + + + + + + + + + {/*
*/} + + {/*
*/} +
+
+ + 开启批量删除 + { + setEnableBatchDelete(v); + }} + > + - - - - - - - - - - - - - {/*
*/} - - {/*
*/} -
-
- - 开启批量删除 - { - setEnableBatchDelete(v); - }} - > - - - - - - - -
- + + +
+ ); }; From e614ca370a928605a433162b0d81e766cd8c14a0 Mon Sep 17 00:00:00 2001 From: FENG Date: Tue, 6 Aug 2024 21:30:20 +0800 Subject: [PATCH 14/22] fix: optionList bug --- web/src/components/ChannelsTable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js index c98f73f..2942a0b 100644 --- a/web/src/components/ChannelsTable.js +++ b/web/src/components/ChannelsTable.js @@ -745,7 +745,7 @@ const ChannelsTable = () => { { setSearchGroup(v); From 93c6d765c71f1fdd344597127133421e3d2d99ff Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Wed, 7 Aug 2024 02:49:02 +0800 Subject: [PATCH 15/22] feat: support gpt-4o-2024-08-06 --- common/model-ratio.go | 17 +++++++++-------- relay/channel/openai/constant.go | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/common/model-ratio.go b/common/model-ratio.go index 68b2d65..3bdd5f7 100644 --- a/common/model-ratio.go +++ b/common/model-ratio.go @@ -32,13 +32,14 @@ var defaultModelRatio = map[string]float64{ "gpt-4-32k": 30, //"gpt-4-32k-0314": 30, //deprecated "gpt-4-32k-0613": 30, - "gpt-4-1106-preview": 5, // $0.01 / 1K tokens - "gpt-4-0125-preview": 5, // $0.01 / 1K tokens - "gpt-4-turbo-preview": 5, // $0.01 / 1K tokens - "gpt-4-vision-preview": 5, // $0.01 / 1K tokens - "gpt-4-1106-vision-preview": 5, // $0.01 / 1K tokens - "gpt-4o": 2.5, // $0.01 / 1K tokens - "gpt-4o-2024-05-13": 2.5, // $0.01 / 1K tokens + "gpt-4-1106-preview": 5, // $0.01 / 1K tokens + "gpt-4-0125-preview": 5, // $0.01 / 1K tokens + "gpt-4-turbo-preview": 5, // $0.01 / 1K tokens + "gpt-4-vision-preview": 5, // $0.01 / 1K tokens + "gpt-4-1106-vision-preview": 5, // $0.01 / 1K tokens + "gpt-4o": 2.5, // $0.01 / 1K tokens + "gpt-4o-2024-05-13": 2.5, // $0.01 / 1K tokens + "gpt-4o-2024-08-06": 1.25, // $0.01 / 1K tokens "gpt-4o-mini": 0.075, "gpt-4o-mini-2024-07-18": 0.075, "gpt-4-turbo": 5, // $0.01 / 1K tokens @@ -326,7 +327,7 @@ func GetCompletionRatio(name string) float64 { return 3 } if strings.HasPrefix(name, "gpt-4o") { - if strings.Contains(name, "mini") { + if strings.HasPrefix(name, "gpt-4o-mini") || name == "gpt-4o-2024-08-06" { return 4 } return 3 diff --git a/relay/channel/openai/constant.go b/relay/channel/openai/constant.go index 50abc2e..81eb93c 100644 --- a/relay/channel/openai/constant.go +++ b/relay/channel/openai/constant.go @@ -8,7 +8,7 @@ var ModelList = []string{ "gpt-4-32k", "gpt-4-32k-0613", "gpt-4-turbo-preview", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-vision-preview", - "gpt-4o", "gpt-4o-2024-05-13", + "gpt-4o", "gpt-4o-2024-05-13", "gpt-4o-2024-08-06", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", "text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large", "text-curie-001", "text-babbage-001", "text-ada-001", From 4490258104bec2673ca22c0bec391ecb0d02fb97 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Wed, 7 Aug 2024 02:50:22 +0800 Subject: [PATCH 16/22] fix bug --- middleware/auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/auth.go b/middleware/auth.go index d2c9b3c..f9a5900 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -146,7 +146,7 @@ func TokenAuth() func(c *gin.Context) { if token != nil { id := c.GetInt("id") if id == 0 { - c.Set("id", token.Id) + c.Set("id", token.UserId) } } if err != nil { From 2e3c266bd6919c796028ed50ec6c59c1c5146122 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Wed, 7 Aug 2024 15:43:01 +0800 Subject: [PATCH 17/22] fix: response format --- dto/text_request.go | 50 ++++++++++++++++++------------------- relay/channel/ollama/dto.go | 24 +++++++++--------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/dto/text_request.go b/dto/text_request.go index 2170e71..a804e63 100644 --- a/dto/text_request.go +++ b/dto/text_request.go @@ -7,31 +7,31 @@ type ResponseFormat struct { } type GeneralOpenAIRequest struct { - Model string `json:"model,omitempty"` - Messages []Message `json:"messages,omitempty"` - Prompt any `json:"prompt,omitempty"` - Stream bool `json:"stream,omitempty"` - StreamOptions *StreamOptions `json:"stream_options,omitempty"` - MaxTokens uint `json:"max_tokens,omitempty"` - Temperature float64 `json:"temperature,omitempty"` - TopP float64 `json:"top_p,omitempty"` - TopK int `json:"top_k,omitempty"` - Stop any `json:"stop,omitempty"` - N int `json:"n,omitempty"` - Input any `json:"input,omitempty"` - Instruction string `json:"instruction,omitempty"` - Size string `json:"size,omitempty"` - Functions any `json:"functions,omitempty"` - FrequencyPenalty float64 `json:"frequency_penalty,omitempty"` - PresencePenalty float64 `json:"presence_penalty,omitempty"` - ResponseFormat *ResponseFormat `json:"response_format,omitempty"` - Seed float64 `json:"seed,omitempty"` - Tools []ToolCall `json:"tools,omitempty"` - ToolChoice any `json:"tool_choice,omitempty"` - User string `json:"user,omitempty"` - LogProbs bool `json:"logprobs,omitempty"` - TopLogProbs int `json:"top_logprobs,omitempty"` - Dimensions int `json:"dimensions,omitempty"` + Model string `json:"model,omitempty"` + Messages []Message `json:"messages,omitempty"` + Prompt any `json:"prompt,omitempty"` + Stream bool `json:"stream,omitempty"` + StreamOptions *StreamOptions `json:"stream_options,omitempty"` + MaxTokens uint `json:"max_tokens,omitempty"` + Temperature float64 `json:"temperature,omitempty"` + TopP float64 `json:"top_p,omitempty"` + TopK int `json:"top_k,omitempty"` + Stop any `json:"stop,omitempty"` + N int `json:"n,omitempty"` + Input any `json:"input,omitempty"` + Instruction string `json:"instruction,omitempty"` + Size string `json:"size,omitempty"` + Functions any `json:"functions,omitempty"` + FrequencyPenalty float64 `json:"frequency_penalty,omitempty"` + PresencePenalty float64 `json:"presence_penalty,omitempty"` + ResponseFormat any `json:"response_format,omitempty"` + Seed float64 `json:"seed,omitempty"` + Tools []ToolCall `json:"tools,omitempty"` + ToolChoice any `json:"tool_choice,omitempty"` + User string `json:"user,omitempty"` + LogProbs bool `json:"logprobs,omitempty"` + TopLogProbs int `json:"top_logprobs,omitempty"` + Dimensions int `json:"dimensions,omitempty"` } type OpenAITools struct { diff --git a/relay/channel/ollama/dto.go b/relay/channel/ollama/dto.go index 4f99a24..fac6b7f 100644 --- a/relay/channel/ollama/dto.go +++ b/relay/channel/ollama/dto.go @@ -3,18 +3,18 @@ package ollama import "one-api/dto" type OllamaRequest struct { - Model string `json:"model,omitempty"` - Messages []dto.Message `json:"messages,omitempty"` - Stream bool `json:"stream,omitempty"` - Temperature float64 `json:"temperature,omitempty"` - Seed float64 `json:"seed,omitempty"` - Topp float64 `json:"top_p,omitempty"` - TopK int `json:"top_k,omitempty"` - Stop any `json:"stop,omitempty"` - Tools []dto.ToolCall `json:"tools,omitempty"` - ResponseFormat *dto.ResponseFormat `json:"response_format,omitempty"` - FrequencyPenalty float64 `json:"frequency_penalty,omitempty"` - PresencePenalty float64 `json:"presence_penalty,omitempty"` + Model string `json:"model,omitempty"` + Messages []dto.Message `json:"messages,omitempty"` + Stream bool `json:"stream,omitempty"` + Temperature float64 `json:"temperature,omitempty"` + Seed float64 `json:"seed,omitempty"` + Topp float64 `json:"top_p,omitempty"` + TopK int `json:"top_k,omitempty"` + Stop any `json:"stop,omitempty"` + Tools []dto.ToolCall `json:"tools,omitempty"` + ResponseFormat any `json:"response_format,omitempty"` + FrequencyPenalty float64 `json:"frequency_penalty,omitempty"` + PresencePenalty float64 `json:"presence_penalty,omitempty"` } type OllamaEmbeddingRequest struct { From 04f0084d978747a17cdd6f6134b6047512cbe491 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Thu, 8 Aug 2024 20:45:41 +0800 Subject: [PATCH 18/22] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dmysql=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/channel.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/model/channel.go b/model/channel.go index 8000537..87610a4 100644 --- a/model/channel.go +++ b/model/channel.go @@ -90,11 +90,13 @@ func GetAllChannels(startIdx int, num int, selectAll bool, idSort bool) ([]*Chan func SearchChannels(keyword string, group string, model string) ([]*Channel, error) { var channels []*Channel keyCol := "`key`" + groupCol := "`group`" modelsCol := "`models`" // 如果是 PostgreSQL,使用双引号 if common.UsingPostgreSQL { keyCol = `"key"` + groupCol = `"group"` modelsCol = `"models"` } @@ -105,7 +107,13 @@ func SearchChannels(keyword string, group string, model string) ([]*Channel, err var whereClause string var args []interface{} if group != "" && group != "null" { - whereClause = "(id = ? OR name LIKE ? OR " + keyCol + " = ?) AND " + modelsCol + ` LIKE ? AND (',' || "group" || ',') LIKE ?` + var groupCondition string + if common.UsingPostgreSQL { + groupCondition = `(',' || ` + groupCol + ` || ',') LIKE ?` + } else { + groupCondition = `CONCAT(',', ` + groupCol + `, ',') LIKE ?` + } + whereClause = "(id = ? OR name LIKE ? OR " + keyCol + " = ?) AND " + modelsCol + ` LIKE ? AND ` + groupCondition args = append(args, common.String2Int(keyword), "%"+keyword+"%", keyword, "%"+model+"%", "%,"+group+",%") } else { whereClause = "(id = ? OR name LIKE ? OR " + keyCol + " = ?) AND " + modelsCol + " LIKE ?" From 9452be51b9b0759dc41485c7bf62b8fbffb39020 Mon Sep 17 00:00:00 2001 From: FENG Date: Fri, 9 Aug 2024 11:38:53 +0800 Subject: [PATCH 19/22] =?UTF-8?q?fix:=20sqlite=20group=20=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/channel.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/model/channel.go b/model/channel.go index 87610a4..34aae68 100644 --- a/model/channel.go +++ b/model/channel.go @@ -108,10 +108,11 @@ func SearchChannels(keyword string, group string, model string) ([]*Channel, err var args []interface{} if group != "" && group != "null" { var groupCondition string - if common.UsingPostgreSQL { - groupCondition = `(',' || ` + groupCol + ` || ',') LIKE ?` - } else { + if common.UsingMySQL { groupCondition = `CONCAT(',', ` + groupCol + `, ',') LIKE ?` + } else { + // sqlite, PostgreSQL + groupCondition = `(',' || ` + groupCol + ` || ',') LIKE ?` } whereClause = "(id = ? OR name LIKE ? OR " + keyCol + " = ?) AND " + modelsCol + ` LIKE ? AND ` + groupCondition args = append(args, common.String2Int(keyword), "%"+keyword+"%", keyword, "%"+model+"%", "%,"+group+",%") @@ -121,7 +122,7 @@ func SearchChannels(keyword string, group string, model string) ([]*Channel, err } // 执行查询 - err := baseQuery.Where(whereClause, args...).Find(&channels).Error + err := baseQuery.Where(whereClause, args...).Order("priority desc").Find(&channels).Error if err != nil { return nil, err } From 9e45dbe96467d3af2dcc609cb0f7a3e4a9e8a22c Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Fri, 9 Aug 2024 16:14:05 +0800 Subject: [PATCH 20/22] fix: close #422 --- model/log.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/model/log.go b/model/log.go index d2e5b84..1076145 100644 --- a/model/log.go +++ b/model/log.go @@ -193,8 +193,8 @@ func SumUsedQuota(logType int, startTimestamp int64, endTimestamp int64, modelNa tx = tx.Where("created_at <= ?", endTimestamp) } if modelName != "" { - tx = tx.Where("model_name = ?", modelName) - rpmTpmQuery = rpmTpmQuery.Where("model_name = ?", modelName) + tx = tx.Where("model_name like ?", modelName) + rpmTpmQuery = rpmTpmQuery.Where("model_name like ?", modelName) } if channel != 0 { tx = tx.Where("channel_id = ?", channel) From 6eab0cc370dee4481aaf9c422a71d961c6ea66cc Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Fri, 9 Aug 2024 18:34:51 +0800 Subject: [PATCH 21/22] =?UTF-8?q?feat:=20=E5=8C=BA=E5=88=86=E9=A2=9D?= =?UTF-8?q?=E5=BA=A6=E4=B8=8D=E8=B6=B3=E5=92=8C=E9=A2=84=E6=89=A3=E8=B4=B9?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- relay/relay-audio.go | 2 +- relay/relay-image.go | 2 +- relay/relay-text.go | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/relay/relay-audio.go b/relay/relay-audio.go index b2fadcc..437c1dd 100644 --- a/relay/relay-audio.go +++ b/relay/relay-audio.go @@ -75,7 +75,7 @@ func AudioHelper(c *gin.Context) *dto.OpenAIErrorWithStatusCode { return service.OpenAIErrorWrapperLocal(err, "get_user_quota_failed", http.StatusInternalServerError) } if userQuota-preConsumedQuota < 0 { - return service.OpenAIErrorWrapperLocal(errors.New("user quota is not enough"), "insufficient_user_quota", http.StatusForbidden) + return service.OpenAIErrorWrapperLocal(errors.New("pre-consumed quota is more than user quota"), "insufficient_user_quota", http.StatusForbidden) } err = model.CacheDecreaseUserQuota(relayInfo.UserId, preConsumedQuota) if err != nil { diff --git a/relay/relay-image.go b/relay/relay-image.go index 83c7538..430d28f 100644 --- a/relay/relay-image.go +++ b/relay/relay-image.go @@ -125,7 +125,7 @@ func ImageHelper(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusCode { quota := int(imageRatio * groupRatio * common.QuotaPerUnit) if userQuota-quota < 0 { - return service.OpenAIErrorWrapperLocal(errors.New("user quota is not enough"), "insufficient_user_quota", http.StatusForbidden) + return service.OpenAIErrorWrapperLocal(errors.New("pre-consumed quota is more than user quota"), "insufficient_user_quota", http.StatusForbidden) } adaptor := GetAdaptor(relayInfo.ApiType) diff --git a/relay/relay-text.go b/relay/relay-text.go index e7c5388..a6a7b94 100644 --- a/relay/relay-text.go +++ b/relay/relay-text.go @@ -238,9 +238,12 @@ func preConsumeQuota(c *gin.Context, preConsumedQuota int, relayInfo *relaycommo if err != nil { return 0, 0, service.OpenAIErrorWrapperLocal(err, "get_user_quota_failed", http.StatusInternalServerError) } - if userQuota <= 0 || userQuota-preConsumedQuota < 0 { + if userQuota <= 0 { return 0, 0, service.OpenAIErrorWrapperLocal(errors.New("user quota is not enough"), "insufficient_user_quota", http.StatusForbidden) } + if userQuota-preConsumedQuota < 0 { + return 0, 0, service.OpenAIErrorWrapperLocal(errors.New("pre-consumed quota is more than user quota"), "insufficient_user_quota", http.StatusForbidden) + } err = model.CacheDecreaseUserQuota(relayInfo.UserId, preConsumedQuota) if err != nil { return 0, 0, service.OpenAIErrorWrapperLocal(err, "decrease_user_quota_failed", http.StatusInternalServerError) From 4b5303a77b776b929dcd12b02b4952550ff730a6 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Fri, 9 Aug 2024 18:48:13 +0800 Subject: [PATCH 22/22] =?UTF-8?q?feat:=20=E5=8C=BA=E5=88=86=E9=A2=9D?= =?UTF-8?q?=E5=BA=A6=E4=B8=8D=E8=B6=B3=E5=92=8C=E9=A2=84=E6=89=A3=E8=B4=B9?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- relay/relay-audio.go | 2 +- relay/relay-image.go | 2 +- relay/relay-text.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/relay/relay-audio.go b/relay/relay-audio.go index 437c1dd..5dbc323 100644 --- a/relay/relay-audio.go +++ b/relay/relay-audio.go @@ -75,7 +75,7 @@ func AudioHelper(c *gin.Context) *dto.OpenAIErrorWithStatusCode { return service.OpenAIErrorWrapperLocal(err, "get_user_quota_failed", http.StatusInternalServerError) } if userQuota-preConsumedQuota < 0 { - return service.OpenAIErrorWrapperLocal(errors.New("pre-consumed quota is more than user quota"), "insufficient_user_quota", http.StatusForbidden) + return service.OpenAIErrorWrapperLocal(errors.New(fmt.Sprintf("audio pre-consumed quota failed, user quota: %d, need quota: %d", userQuota, preConsumedQuota)), "insufficient_user_quota", http.StatusBadRequest) } err = model.CacheDecreaseUserQuota(relayInfo.UserId, preConsumedQuota) if err != nil { diff --git a/relay/relay-image.go b/relay/relay-image.go index 430d28f..74d6c30 100644 --- a/relay/relay-image.go +++ b/relay/relay-image.go @@ -125,7 +125,7 @@ func ImageHelper(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusCode { quota := int(imageRatio * groupRatio * common.QuotaPerUnit) if userQuota-quota < 0 { - return service.OpenAIErrorWrapperLocal(errors.New("pre-consumed quota is more than user quota"), "insufficient_user_quota", http.StatusForbidden) + return service.OpenAIErrorWrapperLocal(errors.New(fmt.Sprintf("image pre-consumed quota failed, user quota: %d, need quota: %d", userQuota, quota)), "insufficient_user_quota", http.StatusBadRequest) } adaptor := GetAdaptor(relayInfo.ApiType) diff --git a/relay/relay-text.go b/relay/relay-text.go index a6a7b94..93d202d 100644 --- a/relay/relay-text.go +++ b/relay/relay-text.go @@ -242,7 +242,7 @@ func preConsumeQuota(c *gin.Context, preConsumedQuota int, relayInfo *relaycommo return 0, 0, service.OpenAIErrorWrapperLocal(errors.New("user quota is not enough"), "insufficient_user_quota", http.StatusForbidden) } if userQuota-preConsumedQuota < 0 { - return 0, 0, service.OpenAIErrorWrapperLocal(errors.New("pre-consumed quota is more than user quota"), "insufficient_user_quota", http.StatusForbidden) + return 0, 0, service.OpenAIErrorWrapperLocal(errors.New(fmt.Sprintf("chat pre-consumed quota failed, user quota: %d, need quota: %d", userQuota, preConsumedQuota)), "insufficient_user_quota", http.StatusBadRequest) } err = model.CacheDecreaseUserQuota(relayInfo.UserId, preConsumedQuota) if err != nil {