diff --git a/common/constants.go b/common/constants.go index fe412e1a..d07dcc8a 100644 --- a/common/constants.go +++ b/common/constants.go @@ -156,9 +156,10 @@ const ( ) const ( - ChannelStatusUnknown = 0 - ChannelStatusEnabled = 1 // don't use 0, 0 is the default value! - ChannelStatusDisabled = 2 // also don't use 0 + ChannelStatusUnknown = 0 + ChannelStatusEnabled = 1 // don't use 0, 0 is the default value! + ChannelStatusManuallyDisabled = 2 // also don't use 0 + ChannelStatusAutoDisabled = 3 ) const ( diff --git a/controller/channel-test.go b/controller/channel-test.go index f7a565a2..1974ef6e 100644 --- a/controller/channel-test.go +++ b/controller/channel-test.go @@ -141,7 +141,7 @@ func disableChannel(channelId int, channelName string, reason string) { if common.RootUserEmail == "" { common.RootUserEmail = model.GetRootUserEmail() } - model.UpdateChannelStatusById(channelId, common.ChannelStatusDisabled) + model.UpdateChannelStatusById(channelId, common.ChannelStatusAutoDisabled) subject := fmt.Sprintf("通道「%s」(#%d)已被禁用", channelName, channelId) content := fmt.Sprintf("通道「%s」(#%d)已被禁用,原因:%s", channelName, channelId, reason) err := common.SendEmail(subject, common.RootUserEmail, content) diff --git a/controller/channel.go b/controller/channel.go index 50b2b5f6..41a55550 100644 --- a/controller/channel.go +++ b/controller/channel.go @@ -127,6 +127,23 @@ func DeleteChannel(c *gin.Context) { return } +func DeleteManuallyDisabledChannel(c *gin.Context) { + rows, err := model.DeleteChannelByStatus(common.ChannelStatusManuallyDisabled) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "", + "data": rows, + }) + return +} + func UpdateChannel(c *gin.Context) { channel := model.Channel{} err := c.ShouldBindJSON(&channel) diff --git a/model/channel.go b/model/channel.go index 61fe9093..36bb78a5 100644 --- a/model/channel.go +++ b/model/channel.go @@ -176,3 +176,8 @@ func updateChannelUsedQuota(id int, quota int) { common.SysError("failed to update channel used quota: " + err.Error()) } } + +func DeleteChannelByStatus(status int64) (int64, error) { + result := DB.Where("status = ?", status).Delete(&Channel{}) + return result.RowsAffected, result.Error +} diff --git a/router/api-router.go b/router/api-router.go index d12bc54b..5ec385dc 100644 --- a/router/api-router.go +++ b/router/api-router.go @@ -74,6 +74,7 @@ func SetApiRouter(router *gin.Engine) { channelRoute.GET("/update_balance/:id", controller.UpdateChannelBalance) channelRoute.POST("/", controller.AddChannel) channelRoute.PUT("/", controller.UpdateChannel) + channelRoute.DELETE("/manually_disabled", controller.DeleteManuallyDisabledChannel) channelRoute.DELETE("/:id", controller.DeleteChannel) } tokenRoute := apiRouter.Group("/token") diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js index 7c8457d0..57d45c55 100644 --- a/web/src/components/ChannelsTable.js +++ b/web/src/components/ChannelsTable.js @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import {Button, Form, Input, Label, Pagination, Popup, Table} from 'semantic-ui-react'; +import { Button, Form, Input, Label, Pagination, Popup, Table } from 'semantic-ui-react'; import { Link } from 'react-router-dom'; import { API, showError, showInfo, showNotice, showSuccess, timestamp2string } from '../helpers'; @@ -96,7 +96,7 @@ const ChannelsTable = () => { }); }, []); - const manageChannel = async (id, action, idx, priority) => { + const manageChannel = async (id, action, idx, value) => { let data = { id }; let res; switch (action) { @@ -112,10 +112,20 @@ const ChannelsTable = () => { res = await API.put('/api/channel/', data); break; case 'priority': - if (priority === '') { + if (value === '') { return; } - data.priority = parseInt(priority); + data.priority = parseInt(value); + res = await API.put('/api/channel/', data); + break; + case 'weight': + if (value === '') { + return; + } + data.weight = parseInt(value); + if (data.weight < 0) { + data.weight = 0; + } res = await API.put('/api/channel/', data); break; } @@ -142,9 +152,23 @@ const ChannelsTable = () => { return ; case 2: return ( - + + 已禁用 + } + content='本渠道被手动禁用' + basic + /> + ); + case 3: + return ( + + 已禁用 + } + content='本渠道被程序自动禁用' + basic + /> ); default: return ( @@ -202,7 +226,7 @@ const ChannelsTable = () => { showInfo(`通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。`); } else { showError(message); - showNotice("当前版本测试是通过按照 OpenAI API 格式使用 gpt-3.5-turbo 模型进行非流式请求实现的,因此测试报错并不一定代表通道不可用,该功能后续会修复。") + showNotice('当前版本测试是通过按照 OpenAI API 格式使用 gpt-3.5-turbo 模型进行非流式请求实现的,因此测试报错并不一定代表通道不可用,该功能后续会修复。'); } }; @@ -216,6 +240,17 @@ const ChannelsTable = () => { } }; + const deleteAllManuallyDisabledChannels = async () => { + const res = await API.delete(`/api/channel/manually_disabled`); + const { success, message, data } = res.data; + if (success) { + showSuccess(`已删除所有手动禁用渠道,共计 ${data} 个`); + await refresh(); + } else { + showError(message); + } + }; + const updateChannelBalance = async (id, name, idx) => { const res = await API.get(`/api/channel/update_balance/${id}/`); const { success, message, balance } = res.data; @@ -343,10 +378,10 @@ const ChannelsTable = () => { 余额 { - sortChannel('priority'); - }} + style={{ cursor: 'pointer' }} + onClick={() => { + sortChannel('priority'); + }} > 优先级 @@ -390,18 +425,18 @@ const ChannelsTable = () => { { - manageChannel( - channel.id, - 'priority', - idx, - event.target.value, - ); - }}> - - } - content='渠道选择优先级,越高越优先' - basic + trigger={ { + manageChannel( + channel.id, + 'priority', + idx, + event.target.value + ); + }}> + + } + content='渠道选择优先级,越高越优先' + basic /> @@ -481,6 +516,20 @@ const ChannelsTable = () => { + + 删除所有手动禁用渠道 + + } + on='click' + flowing + hoverable + > + +