Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
9d3bd127
authored
Jun 16, 2025
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
✨
feat(channel): enhance channel handling with multi-key support
parent
52008cbb
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
115 additions
and
12 deletions
+115
-12
controller/channel.go
+16
-0
model/channel.go
+18
-5
web/src/components/table/ChannelsTable.js
+80
-6
web/src/pages/Channel/EditChannel.js
+1
-1
No files found.
controller/channel.go
View file @
9d3bd127
...
...
@@ -318,6 +318,22 @@ func AddChannel(c *gin.Context) {
})
return
}
toMap
:=
common
.
StrToMap
(
addChannelRequest
.
Channel
.
Key
)
if
toMap
!=
nil
{
addChannelRequest
.
Channel
.
ChannelInfo
.
MultiKeySize
=
len
(
toMap
)
}
else
{
addChannelRequest
.
Channel
.
ChannelInfo
.
MultiKeySize
=
0
}
}
else
{
cleanKeys
:=
make
([]
string
,
0
)
for
_
,
key
:=
range
strings
.
Split
(
addChannelRequest
.
Channel
.
Key
,
"
\n
"
)
{
if
key
==
""
{
continue
}
cleanKeys
=
append
(
cleanKeys
,
key
)
}
addChannelRequest
.
Channel
.
ChannelInfo
.
MultiKeySize
=
len
(
cleanKeys
)
addChannelRequest
.
Channel
.
Key
=
strings
.
Join
(
cleanKeys
,
"
\n
"
)
}
keys
=
[]
string
{
addChannelRequest
.
Channel
.
Key
}
case
"batch"
:
...
...
model/channel.go
View file @
9d3bd127
package
model
import
(
"database/sql/driver"
"encoding/json"
"one-api/common"
"strings"
...
...
@@ -9,11 +10,6 @@ import (
"gorm.io/gorm"
)
type
ChannelInfo
struct
{
MultiKeyMode
bool
`json:"multi_key_mode"`
// 是否多Key模式
MultiKeyStatusList
map
[
int
]
int
`json:"multi_key_status_list"`
// key状态列表,key index -> status
}
type
Channel
struct
{
Id
int
`json:"id"`
Type
int
`json:"type" gorm:"default:0"`
...
...
@@ -46,6 +42,23 @@ type Channel struct {
ChannelInfo
ChannelInfo
`json:"channel_info" gorm:"type:json"`
}
type
ChannelInfo
struct
{
MultiKeyMode
bool
`json:"multi_key_mode"`
// 是否多Key模式
MultiKeyStatusList
map
[
int
]
int
`json:"multi_key_status_list"`
// key状态列表,key index -> status
MultiKeySize
int
`json:"multi_key_size"`
// 多Key模式下的key数量
}
// Value implements driver.Valuer interface
func
(
c
ChannelInfo
)
Value
()
(
driver
.
Value
,
error
)
{
return
json
.
Marshal
(
c
)
}
// Scan implements sql.Scanner interface
func
(
c
*
ChannelInfo
)
Scan
(
value
interface
{})
error
{
bytesValue
,
_
:=
value
.
([]
byte
)
return
json
.
Unmarshal
(
bytesValue
,
c
)
}
func
(
channel
*
Channel
)
GetModels
()
[]
string
{
if
channel
.
Models
==
""
{
return
[]
string
{}
...
...
web/src/components/table/ChannelsTable.js
View file @
9d3bd127
...
...
@@ -22,7 +22,7 @@ import {
Clock
,
AlertTriangle
,
Coins
,
Tags
Tags
,
Boxes
}
from
'lucide-react'
;
import
{
CHANNEL_OPTIONS
,
ITEMS_PER_PAGE
}
from
'../../constants/index.js'
;
...
...
@@ -73,7 +73,7 @@ const ChannelsTable = () => {
let
type2label
=
undefined
;
const
renderType
=
(
type
)
=>
{
const
renderType
=
(
type
,
multiKeyMode
=
false
)
=>
{
if
(
!
type2label
)
{
type2label
=
new
Map
();
for
(
let
i
=
0
;
i
<
CHANNEL_OPTIONS
.
length
;
i
++
)
{
...
...
@@ -81,6 +81,24 @@ const ChannelsTable = () => {
}
type2label
[
0
]
=
{
value
:
0
,
label
:
t
(
'未知类型'
),
color
:
'grey'
};
}
if
(
multiKeyMode
)
{
return
(
<
Tag
size
=
'large'
color
=
{
type2label
[
type
]?.
color
}
shape
=
'circle'
prefixIcon
=
{
<
div
className
=
"flex items-center"
>
<
Boxes
size
=
{
14
}
className
=
"mr-1"
/>
{
getChannelIcon
(
type
)}
<
/div
>
}
>
{
type2label
[
type
]?.
label
}
<
/Tag
>
);
}
return
(
<
Tag
size
=
'large'
...
...
@@ -107,7 +125,63 @@ const ChannelsTable = () => {
);
};
const
renderStatus
=
(
status
)
=>
{
const
renderMultiKeyStatus
=
(
status
,
channelInfo
)
=>
{
if
(
!
channelInfo
||
!
channelInfo
.
multi_key_mode
)
{
return
renderStatus
(
status
,
channelInfo
);
}
const
{
multi_key_status_list
,
multi_key_size
}
=
channelInfo
;
const
totalCount
=
multi_key_size
||
0
;
// If multi_key_status_list is null, it means all keys are enabled
if
(
!
multi_key_status_list
)
{
return
(
<
Tag
size
=
'large'
color
=
'green'
shape
=
'circle'
prefixIcon
=
{
<
CheckCircle
size
=
{
14
}
/>}
>
{
t
(
'已启用'
)}:{
totalCount
}
/{totalCount
}
<
/Tag
>
);
}
// Count enabled keys from the status map
const
statusValues
=
Object
.
values
(
multi_key_status_list
);
const
enabledCount
=
statusValues
.
filter
(
s
=>
s
===
1
).
length
;
// Determine status text, color and icon based on enabled ratio
let
statusText
,
statusColor
,
statusIcon
;
const
enabledRatio
=
totalCount
>
0
?
enabledCount
/
totalCount
:
0
;
if
(
enabledCount
===
totalCount
)
{
statusText
=
t
(
'已启用'
);
statusColor
=
'green'
;
statusIcon
=
<
CheckCircle
size
=
{
14
}
/>
;
}
else
if
(
enabledCount
===
0
)
{
statusText
=
t
(
'已禁用'
);
statusColor
=
'red'
;
statusIcon
=
<
XCircle
size
=
{
14
}
/>
;
}
else
{
statusText
=
t
(
'部分启用'
);
// Color based on percentage: green (>80%), yellow (20-80%), red (<20%)
if
(
enabledRatio
>
0.8
)
{
statusColor
=
'green'
;
}
else
if
(
enabledRatio
>=
0.2
)
{
statusColor
=
'yellow'
;
}
else
{
statusColor
=
'red'
;
}
statusIcon
=
<
AlertCircle
size
=
{
14
}
/>
;
}
return
(
<
Tag
size
=
'large'
color
=
{
statusColor
}
shape
=
'circle'
prefixIcon
=
{
statusIcon
}
>
{
statusText
}:{
enabledCount
}
/{totalCount
}
<
/Tag
>
);
};
const
renderStatus
=
(
status
,
channelInfo
=
undefined
)
=>
{
if
(
channelInfo
?.
multi_key_mode
)
{
return
renderMultiKeyStatus
(
status
,
channelInfo
);
}
switch
(
status
)
{
case
1
:
return
(
...
...
@@ -297,7 +371,7 @@ const ChannelsTable = () => {
dataIndex
:
'type'
,
render
:
(
text
,
record
,
index
)
=>
{
if
(
record
.
children
===
undefined
)
{
return
<>
{
renderType
(
text
)}
<
/>
;
return
<>
{
renderType
(
text
,
record
.
channel_info
?.
multi_key_mode
)}
<
/>
;
}
else
{
return
<>
{
renderTagType
()}
<
/>
;
}
...
...
@@ -320,12 +394,12 @@ const ChannelsTable = () => {
<
Tooltip
content
=
{
t
(
'原因:'
)
+
reason
+
t
(
',时间:'
)
+
timestamp2string
(
time
)}
>
{
renderStatus
(
text
)}
{
renderStatus
(
text
,
record
.
channel_info
)}
<
/Tooltip
>
<
/div
>
);
}
else
{
return
renderStatus
(
text
);
return
renderStatus
(
text
,
record
.
channel_info
);
}
},
},
...
...
web/src/pages/Channel/EditChannel.js
View file @
9d3bd127
...
...
@@ -664,7 +664,7 @@ const EditChannel = (props) => {
onChange
=
{()
=>
setMergeToSingle
(
!
mergeToSingle
)}
/
>
<
Text
style
=
{{
fontSize
:
12
}}
className
=
"ml-2 text-gray-600"
>
{
t
(
'合并为单通道(多 Key 模式)'
)}
{
t
(
'合并为单通道(多 Key
聚合
模式)'
)}
<
/Text
>
<
/div
>
)}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment