Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
dc447cce
Unverified
Commit
dc447cce
authored
Mar 19, 2026
by
Wesley Liddick
Committed by
GitHub
Mar 19, 2026
Browse files
Merge pull request #1153 from hging/main
feat: add ungrouped filter to account
parents
7ec29638
8027531d
Changes
9
Hide whitespace changes
Inline
Side-by-side
backend/internal/handler/admin/account_handler.go
View file @
dc447cce
...
@@ -165,6 +165,8 @@ type AccountWithConcurrency struct {
...
@@ -165,6 +165,8 @@ type AccountWithConcurrency struct {
CurrentRPM
*
int
`json:"current_rpm,omitempty"`
// 当前分钟 RPM 计数
CurrentRPM
*
int
`json:"current_rpm,omitempty"`
// 当前分钟 RPM 计数
}
}
const
accountListGroupUngroupedQueryValue
=
"ungrouped"
func
(
h
*
AccountHandler
)
buildAccountResponseWithRuntime
(
ctx
context
.
Context
,
account
*
service
.
Account
)
AccountWithConcurrency
{
func
(
h
*
AccountHandler
)
buildAccountResponseWithRuntime
(
ctx
context
.
Context
,
account
*
service
.
Account
)
AccountWithConcurrency
{
item
:=
AccountWithConcurrency
{
item
:=
AccountWithConcurrency
{
Account
:
dto
.
AccountFromService
(
account
),
Account
:
dto
.
AccountFromService
(
account
),
...
@@ -226,7 +228,20 @@ func (h *AccountHandler) List(c *gin.Context) {
...
@@ -226,7 +228,20 @@ func (h *AccountHandler) List(c *gin.Context) {
var
groupID
int64
var
groupID
int64
if
groupIDStr
:=
c
.
Query
(
"group"
);
groupIDStr
!=
""
{
if
groupIDStr
:=
c
.
Query
(
"group"
);
groupIDStr
!=
""
{
groupID
,
_
=
strconv
.
ParseInt
(
groupIDStr
,
10
,
64
)
if
groupIDStr
==
accountListGroupUngroupedQueryValue
{
groupID
=
service
.
AccountListGroupUngrouped
}
else
{
parsedGroupID
,
parseErr
:=
strconv
.
ParseInt
(
groupIDStr
,
10
,
64
)
if
parseErr
!=
nil
{
response
.
ErrorFrom
(
c
,
infraerrors
.
BadRequest
(
"INVALID_GROUP_FILTER"
,
"invalid group filter"
))
return
}
if
parsedGroupID
<
0
{
response
.
ErrorFrom
(
c
,
infraerrors
.
BadRequest
(
"INVALID_GROUP_FILTER"
,
"invalid group filter"
))
return
}
groupID
=
parsedGroupID
}
}
}
accounts
,
total
,
err
:=
h
.
adminService
.
ListAccounts
(
c
.
Request
.
Context
(),
page
,
pageSize
,
platform
,
accountType
,
status
,
search
,
groupID
)
accounts
,
total
,
err
:=
h
.
adminService
.
ListAccounts
(
c
.
Request
.
Context
(),
page
,
pageSize
,
platform
,
accountType
,
status
,
search
,
groupID
)
...
...
backend/internal/repository/account_repo.go
View file @
dc447cce
...
@@ -474,7 +474,9 @@ func (r *accountRepository) ListWithFilters(ctx context.Context, params paginati
...
@@ -474,7 +474,9 @@ func (r *accountRepository) ListWithFilters(ctx context.Context, params paginati
if
search
!=
""
{
if
search
!=
""
{
q
=
q
.
Where
(
dbaccount
.
NameContainsFold
(
search
))
q
=
q
.
Where
(
dbaccount
.
NameContainsFold
(
search
))
}
}
if
groupID
>
0
{
if
groupID
==
service
.
AccountListGroupUngrouped
{
q
=
q
.
Where
(
dbaccount
.
Not
(
dbaccount
.
HasAccountGroups
()))
}
else
if
groupID
>
0
{
q
=
q
.
Where
(
dbaccount
.
HasAccountGroupsWith
(
dbaccountgroup
.
GroupIDEQ
(
groupID
)))
q
=
q
.
Where
(
dbaccount
.
HasAccountGroupsWith
(
dbaccountgroup
.
GroupIDEQ
(
groupID
)))
}
}
...
...
backend/internal/repository/account_repo_integration_test.go
View file @
dc447cce
...
@@ -214,6 +214,7 @@ func (s *AccountRepoSuite) TestListWithFilters() {
...
@@ -214,6 +214,7 @@ func (s *AccountRepoSuite) TestListWithFilters() {
accType
string
accType
string
status
string
status
string
search
string
search
string
groupID
int64
wantCount
int
wantCount
int
validate
func
(
accounts
[]
service
.
Account
)
validate
func
(
accounts
[]
service
.
Account
)
}{
}{
...
@@ -265,6 +266,21 @@ func (s *AccountRepoSuite) TestListWithFilters() {
...
@@ -265,6 +266,21 @@ func (s *AccountRepoSuite) TestListWithFilters() {
s
.
Require
()
.
Contains
(
accounts
[
0
]
.
Name
,
"alpha"
)
s
.
Require
()
.
Contains
(
accounts
[
0
]
.
Name
,
"alpha"
)
},
},
},
},
{
name
:
"filter_by_ungrouped"
,
setup
:
func
(
client
*
dbent
.
Client
)
{
group
:=
mustCreateGroup
(
s
.
T
(),
client
,
&
service
.
Group
{
Name
:
"g-ungrouped"
})
grouped
:=
mustCreateAccount
(
s
.
T
(),
client
,
&
service
.
Account
{
Name
:
"grouped-account"
})
mustCreateAccount
(
s
.
T
(),
client
,
&
service
.
Account
{
Name
:
"ungrouped-account"
})
mustBindAccountToGroup
(
s
.
T
(),
client
,
grouped
.
ID
,
group
.
ID
,
1
)
},
groupID
:
service
.
AccountListGroupUngrouped
,
wantCount
:
1
,
validate
:
func
(
accounts
[]
service
.
Account
)
{
s
.
Require
()
.
Equal
(
"ungrouped-account"
,
accounts
[
0
]
.
Name
)
s
.
Require
()
.
Empty
(
accounts
[
0
]
.
GroupIDs
)
},
},
}
}
for
_
,
tt
:=
range
tests
{
for
_
,
tt
:=
range
tests
{
...
@@ -277,7 +293,7 @@ func (s *AccountRepoSuite) TestListWithFilters() {
...
@@ -277,7 +293,7 @@ func (s *AccountRepoSuite) TestListWithFilters() {
tt
.
setup
(
client
)
tt
.
setup
(
client
)
accounts
,
_
,
err
:=
repo
.
ListWithFilters
(
ctx
,
pagination
.
PaginationParams
{
Page
:
1
,
PageSize
:
10
},
tt
.
platform
,
tt
.
accType
,
tt
.
status
,
tt
.
search
,
0
)
accounts
,
_
,
err
:=
repo
.
ListWithFilters
(
ctx
,
pagination
.
PaginationParams
{
Page
:
1
,
PageSize
:
10
},
tt
.
platform
,
tt
.
accType
,
tt
.
status
,
tt
.
search
,
tt
.
groupID
)
s
.
Require
()
.
NoError
(
err
)
s
.
Require
()
.
NoError
(
err
)
s
.
Require
()
.
Len
(
accounts
,
tt
.
wantCount
)
s
.
Require
()
.
Len
(
accounts
,
tt
.
wantCount
)
if
tt
.
validate
!=
nil
{
if
tt
.
validate
!=
nil
{
...
...
backend/internal/service/account_service.go
View file @
dc447cce
...
@@ -14,6 +14,8 @@ var (
...
@@ -14,6 +14,8 @@ var (
ErrAccountNilInput
=
infraerrors
.
BadRequest
(
"ACCOUNT_NIL_INPUT"
,
"account input cannot be nil"
)
ErrAccountNilInput
=
infraerrors
.
BadRequest
(
"ACCOUNT_NIL_INPUT"
,
"account input cannot be nil"
)
)
)
const
AccountListGroupUngrouped
int64
=
-
1
type
AccountRepository
interface
{
type
AccountRepository
interface
{
Create
(
ctx
context
.
Context
,
account
*
Account
)
error
Create
(
ctx
context
.
Context
,
account
*
Account
)
error
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
Account
,
error
)
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
Account
,
error
)
...
...
frontend/src/api/admin/accounts.ts
View file @
dc447cce
...
@@ -66,6 +66,7 @@ export async function listWithEtag(
...
@@ -66,6 +66,7 @@ export async function listWithEtag(
platform
?:
string
platform
?:
string
type
?:
string
type
?:
string
status
?:
string
status
?:
string
group
?:
string
search
?:
string
search
?:
string
lite
?:
string
lite
?:
string
},
},
...
...
frontend/src/components/admin/account/AccountTableFilters.vue
View file @
dc447cce
...
@@ -26,5 +26,9 @@ const updateGroup = (value: string | number | boolean | null) => { emit('update:
...
@@ -26,5 +26,9 @@ const updateGroup = (value: string | number | boolean | null) => { emit('update:
const
pOpts
=
computed
(()
=>
[{
value
:
''
,
label
:
t
(
'
admin.accounts.allPlatforms
'
)
},
{
value
:
'
anthropic
'
,
label
:
'
Anthropic
'
},
{
value
:
'
openai
'
,
label
:
'
OpenAI
'
},
{
value
:
'
gemini
'
,
label
:
'
Gemini
'
},
{
value
:
'
antigravity
'
,
label
:
'
Antigravity
'
},
{
value
:
'
sora
'
,
label
:
'
Sora
'
}])
const
pOpts
=
computed
(()
=>
[{
value
:
''
,
label
:
t
(
'
admin.accounts.allPlatforms
'
)
},
{
value
:
'
anthropic
'
,
label
:
'
Anthropic
'
},
{
value
:
'
openai
'
,
label
:
'
OpenAI
'
},
{
value
:
'
gemini
'
,
label
:
'
Gemini
'
},
{
value
:
'
antigravity
'
,
label
:
'
Antigravity
'
},
{
value
:
'
sora
'
,
label
:
'
Sora
'
}])
const
tOpts
=
computed
(()
=>
[{
value
:
''
,
label
:
t
(
'
admin.accounts.allTypes
'
)
},
{
value
:
'
oauth
'
,
label
:
t
(
'
admin.accounts.oauthType
'
)
},
{
value
:
'
setup-token
'
,
label
:
t
(
'
admin.accounts.setupToken
'
)
},
{
value
:
'
apikey
'
,
label
:
t
(
'
admin.accounts.apiKey
'
)
},
{
value
:
'
bedrock
'
,
label
:
'
AWS Bedrock
'
}])
const
tOpts
=
computed
(()
=>
[{
value
:
''
,
label
:
t
(
'
admin.accounts.allTypes
'
)
},
{
value
:
'
oauth
'
,
label
:
t
(
'
admin.accounts.oauthType
'
)
},
{
value
:
'
setup-token
'
,
label
:
t
(
'
admin.accounts.setupToken
'
)
},
{
value
:
'
apikey
'
,
label
:
t
(
'
admin.accounts.apiKey
'
)
},
{
value
:
'
bedrock
'
,
label
:
'
AWS Bedrock
'
}])
const
sOpts
=
computed
(()
=>
[{
value
:
''
,
label
:
t
(
'
admin.accounts.allStatus
'
)
},
{
value
:
'
active
'
,
label
:
t
(
'
admin.accounts.status.active
'
)
},
{
value
:
'
inactive
'
,
label
:
t
(
'
admin.accounts.status.inactive
'
)
},
{
value
:
'
error
'
,
label
:
t
(
'
admin.accounts.status.error
'
)
},
{
value
:
'
rate_limited
'
,
label
:
t
(
'
admin.accounts.status.rateLimited
'
)
},
{
value
:
'
temp_unschedulable
'
,
label
:
t
(
'
admin.accounts.status.tempUnschedulable
'
)
}])
const
sOpts
=
computed
(()
=>
[{
value
:
''
,
label
:
t
(
'
admin.accounts.allStatus
'
)
},
{
value
:
'
active
'
,
label
:
t
(
'
admin.accounts.status.active
'
)
},
{
value
:
'
inactive
'
,
label
:
t
(
'
admin.accounts.status.inactive
'
)
},
{
value
:
'
error
'
,
label
:
t
(
'
admin.accounts.status.error
'
)
},
{
value
:
'
rate_limited
'
,
label
:
t
(
'
admin.accounts.status.rateLimited
'
)
},
{
value
:
'
temp_unschedulable
'
,
label
:
t
(
'
admin.accounts.status.tempUnschedulable
'
)
}])
const
gOpts
=
computed
(()
=>
[{
value
:
''
,
label
:
t
(
'
admin.accounts.allGroups
'
)
},
...(
props
.
groups
||
[]).
map
(
g
=>
({
value
:
String
(
g
.
id
),
label
:
g
.
name
}))])
const
gOpts
=
computed
(()
=>
[
{
value
:
''
,
label
:
t
(
'
admin.accounts.allGroups
'
)
},
{
value
:
'
ungrouped
'
,
label
:
t
(
'
admin.accounts.ungroupedGroup
'
)
},
...(
props
.
groups
||
[]).
map
(
g
=>
({
value
:
String
(
g
.
id
),
label
:
g
.
name
}))
])
</
script
>
</
script
>
frontend/src/i18n/locales/en.ts
View file @
dc447cce
...
@@ -1883,6 +1883,7 @@ export default {
...
@@ -1883,6 +1883,7 @@ export default {
allTypes
:
'
All Types
'
,
allTypes
:
'
All Types
'
,
allStatus
:
'
All Status
'
,
allStatus
:
'
All Status
'
,
allGroups
:
'
All Groups
'
,
allGroups
:
'
All Groups
'
,
ungroupedGroup
:
'
Ungrouped
'
,
oauthType
:
'
OAuth
'
,
oauthType
:
'
OAuth
'
,
setupToken
:
'
Setup Token
'
,
setupToken
:
'
Setup Token
'
,
apiKey
:
'
API Key
'
,
apiKey
:
'
API Key
'
,
...
...
frontend/src/i18n/locales/zh.ts
View file @
dc447cce
...
@@ -1965,6 +1965,7 @@ export default {
...
@@ -1965,6 +1965,7 @@ export default {
allTypes
:
'
全部类型
'
,
allTypes
:
'
全部类型
'
,
allStatus
:
'
全部状态
'
,
allStatus
:
'
全部状态
'
,
allGroups
:
'
全部分组
'
,
allGroups
:
'
全部分组
'
,
ungroupedGroup
:
'
未分配分组
'
,
oauthType
:
'
OAuth
'
,
oauthType
:
'
OAuth
'
,
// Schedulable toggle
// Schedulable toggle
schedulable
:
'
参与调度
'
,
schedulable
:
'
参与调度
'
,
...
...
frontend/src/views/admin/AccountsView.vue
View file @
dc447cce
...
@@ -758,6 +758,7 @@ const refreshAccountsIncrementally = async () => {
...
@@ -758,6 +758,7 @@ const refreshAccountsIncrementally = async () => {
platform
?:
string
platform
?:
string
type
?:
string
type
?:
string
status
?:
string
status
?:
string
group
?:
string
search
?:
string
search
?:
string
}
,
}
,
...
...
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