Skip to content

Commit 50a3c7f

Browse files
geminiwenclaude
andcommitted
feat: add platform type filter to subscription management page
Add a platform filter dropdown to the admin subscriptions view, allowing filtering subscriptions by platform (Anthropic, OpenAI, Gemini, etc.) through the group association. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 045cba7 commit 50a3c7f

9 files changed

Lines changed: 33 additions & 6 deletions

File tree

backend/internal/handler/admin/subscription_handler.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,13 @@ func (h *SubscriptionHandler) List(c *gin.Context) {
7777
}
7878
}
7979
status := c.Query("status")
80+
platform := c.Query("platform")
8081

8182
// Parse sorting parameters
8283
sortBy := c.DefaultQuery("sort_by", "created_at")
8384
sortOrder := c.DefaultQuery("sort_order", "desc")
8485

85-
subscriptions, pagination, err := h.subscriptionService.List(c.Request.Context(), page, pageSize, userID, groupID, status, sortBy, sortOrder)
86+
subscriptions, pagination, err := h.subscriptionService.List(c.Request.Context(), page, pageSize, userID, groupID, status, platform, sortBy, sortOrder)
8687
if err != nil {
8788
response.ErrorFrom(c, err)
8889
return

backend/internal/repository/user_subscription_repo.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"time"
66

77
dbent "github.com/Wei-Shaw/sub2api/ent"
8+
"github.com/Wei-Shaw/sub2api/ent/group"
89
"github.com/Wei-Shaw/sub2api/ent/usersubscription"
910
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
1011
"github.com/Wei-Shaw/sub2api/internal/service"
@@ -190,7 +191,7 @@ func (r *userSubscriptionRepository) ListByGroupID(ctx context.Context, groupID
190191
return userSubscriptionEntitiesToService(subs), paginationResultFromTotal(int64(total), params), nil
191192
}
192193

193-
func (r *userSubscriptionRepository) List(ctx context.Context, params pagination.PaginationParams, userID, groupID *int64, status, sortBy, sortOrder string) ([]service.UserSubscription, *pagination.PaginationResult, error) {
194+
func (r *userSubscriptionRepository) List(ctx context.Context, params pagination.PaginationParams, userID, groupID *int64, status, platform, sortBy, sortOrder string) ([]service.UserSubscription, *pagination.PaginationResult, error) {
194195
client := clientFromContext(ctx, r.client)
195196
q := client.UserSubscription.Query()
196197
if userID != nil {
@@ -199,6 +200,9 @@ func (r *userSubscriptionRepository) List(ctx context.Context, params pagination
199200
if groupID != nil {
200201
q = q.Where(usersubscription.GroupIDEQ(*groupID))
201202
}
203+
if platform != "" {
204+
q = q.Where(usersubscription.HasGroupWith(group.PlatformEQ(platform)))
205+
}
202206

203207
// Status filtering with real-time expiration check
204208
now := time.Now()

backend/internal/service/subscription_assign_idempotency_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ func (userSubRepoNoop) ListActiveByUserID(context.Context, int64) ([]UserSubscri
9292
func (userSubRepoNoop) ListByGroupID(context.Context, int64, pagination.PaginationParams) ([]UserSubscription, *pagination.PaginationResult, error) {
9393
panic("unexpected ListByGroupID call")
9494
}
95-
func (userSubRepoNoop) List(context.Context, pagination.PaginationParams, *int64, *int64, string, string, string) ([]UserSubscription, *pagination.PaginationResult, error) {
95+
func (userSubRepoNoop) List(context.Context, pagination.PaginationParams, *int64, *int64, string, string, string, string) ([]UserSubscription, *pagination.PaginationResult, error) {
9696
panic("unexpected List call")
9797
}
9898
func (userSubRepoNoop) ExistsByUserIDAndGroupID(context.Context, int64, int64) (bool, error) {

backend/internal/service/subscription_service.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -634,9 +634,9 @@ func (s *SubscriptionService) ListGroupSubscriptions(ctx context.Context, groupI
634634
}
635635

636636
// List 获取所有订阅(分页,支持筛选和排序)
637-
func (s *SubscriptionService) List(ctx context.Context, page, pageSize int, userID, groupID *int64, status, sortBy, sortOrder string) ([]UserSubscription, *pagination.PaginationResult, error) {
637+
func (s *SubscriptionService) List(ctx context.Context, page, pageSize int, userID, groupID *int64, status, platform, sortBy, sortOrder string) ([]UserSubscription, *pagination.PaginationResult, error) {
638638
params := pagination.PaginationParams{Page: page, PageSize: pageSize}
639-
subs, pag, err := s.userSubRepo.List(ctx, params, userID, groupID, status, sortBy, sortOrder)
639+
subs, pag, err := s.userSubRepo.List(ctx, params, userID, groupID, status, platform, sortBy, sortOrder)
640640
if err != nil {
641641
return nil, nil, err
642642
}

backend/internal/service/user_subscription_port.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ type UserSubscriptionRepository interface {
1818
ListByUserID(ctx context.Context, userID int64) ([]UserSubscription, error)
1919
ListActiveByUserID(ctx context.Context, userID int64) ([]UserSubscription, error)
2020
ListByGroupID(ctx context.Context, groupID int64, params pagination.PaginationParams) ([]UserSubscription, *pagination.PaginationResult, error)
21-
List(ctx context.Context, params pagination.PaginationParams, userID, groupID *int64, status, sortBy, sortOrder string) ([]UserSubscription, *pagination.PaginationResult, error)
21+
List(ctx context.Context, params pagination.PaginationParams, userID, groupID *int64, status, platform, sortBy, sortOrder string) ([]UserSubscription, *pagination.PaginationResult, error)
2222

2323
ExistsByUserIDAndGroupID(ctx context.Context, userID, groupID int64) (bool, error)
2424
ExtendExpiry(ctx context.Context, subscriptionID int64, newExpiresAt time.Time) error

frontend/src/api/admin/subscriptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export async function list(
2727
status?: 'active' | 'expired' | 'revoked'
2828
user_id?: number
2929
group_id?: number
30+
platform?: string
3031
sort_by?: string
3132
sort_order?: 'asc' | 'desc'
3233
},

frontend/src/i18n/locales/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,6 +1694,7 @@ export default {
16941694
revokeSubscription: 'Revoke Subscription',
16951695
allStatus: 'All Status',
16961696
allGroups: 'All Groups',
1697+
allPlatforms: 'All Platforms',
16971698
daily: 'Daily',
16981699
weekly: 'Weekly',
16991700
monthly: 'Monthly',

frontend/src/i18n/locales/zh.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,6 +1774,7 @@ export default {
17741774
revokeSubscription: '撤销订阅',
17751775
allStatus: '全部状态',
17761776
allGroups: '全部分组',
1777+
allPlatforms: '全部平台',
17771778
daily: '每日',
17781779
weekly: '每周',
17791780
monthly: '每月',

frontend/src/views/admin/SubscriptionsView.vue

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@
8181
@change="applyFilters"
8282
/>
8383
</div>
84+
<div class="w-full sm:w-40">
85+
<Select
86+
v-model="filters.platform"
87+
:options="platformFilterOptions"
88+
:placeholder="t('admin.subscriptions.allPlatforms')"
89+
@change="applyFilters"
90+
/>
91+
</div>
8492
</div>
8593

8694
<!-- Right: Actions -->
@@ -813,6 +821,7 @@ let userSearchTimeout: ReturnType<typeof setTimeout> | null = null
813821
const filters = reactive({
814822
status: 'active',
815823
group_id: '',
824+
platform: '',
816825
user_id: null as number | null
817826
})
818827
@@ -855,6 +864,15 @@ const groupOptions = computed(() => [
855864
...groups.value.map((g) => ({ value: g.id.toString(), label: g.name }))
856865
])
857866
867+
const platformFilterOptions = computed(() => [
868+
{ value: '', label: t('admin.subscriptions.allPlatforms') },
869+
{ value: 'anthropic', label: 'Anthropic' },
870+
{ value: 'openai', label: 'OpenAI' },
871+
{ value: 'gemini', label: 'Gemini' },
872+
{ value: 'antigravity', label: 'Antigravity' },
873+
{ value: 'sora', label: 'Sora' }
874+
])
875+
858876
// Group options for assign (only subscription type groups)
859877
const subscriptionGroupOptions = computed(() =>
860878
groups.value
@@ -890,6 +908,7 @@ const loadSubscriptions = async () => {
890908
{
891909
status: (filters.status as any) || undefined,
892910
group_id: filters.group_id ? parseInt(filters.group_id) : undefined,
911+
platform: filters.platform || undefined,
893912
user_id: filters.user_id || undefined,
894913
sort_by: sortState.sort_by,
895914
sort_order: sortState.sort_order

0 commit comments

Comments
 (0)