Skip to content

Commit 767a41e

Browse files
ischanxclaude
andcommitted
feat: 允许管理员为持有有效订阅的用户绑定订阅类型分组
之前管理员无法通过 API 密钥管理将用户绑定到订阅类型分组(直接返回错误)。 现在改为检查用户是否持有该分组的有效订阅,有则允许绑定,无则拒绝。 - admin_service: 新增 userSubRepo 依赖,替换硬拒绝为订阅校验 - admin_service: 区分 ErrSubscriptionNotFound 和内部错误,避免 DB 故障被误报 - wire_gen/api_contract_test: 同步新增参数 - UserApiKeysModal: 管理员分组下拉不再过滤订阅类型分组 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c8eff34 commit 767a41e

4 files changed

Lines changed: 13 additions & 6 deletions

File tree

backend/cmd/server/wire_gen.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/internal/server/api_contract_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ func newContractDeps(t *testing.T) *contractDeps {
645645
settingRepo := newStubSettingRepo()
646646
settingService := service.NewSettingService(settingRepo, cfg)
647647

648-
adminService := service.NewAdminService(userRepo, groupRepo, &accountRepo, nil, proxyRepo, apiKeyRepo, redeemRepo, nil, nil, nil, nil, nil, nil, nil, nil)
648+
adminService := service.NewAdminService(userRepo, groupRepo, &accountRepo, nil, proxyRepo, apiKeyRepo, redeemRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil)
649649
authHandler := handler.NewAuthHandler(cfg, nil, userService, settingService, nil, redeemService, nil)
650650
apiKeyHandler := handler.NewAPIKeyHandler(apiKeyService)
651651
usageHandler := handler.NewUsageHandler(usageService, apiKeyService)

backend/internal/service/admin_service.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ type adminServiceImpl struct {
432432
entClient *dbent.Client // 用于开启数据库事务
433433
settingService *SettingService
434434
defaultSubAssigner DefaultSubscriptionAssigner
435+
userSubRepo UserSubscriptionRepository
435436
}
436437

437438
type userGroupRateBatchReader interface {
@@ -459,6 +460,7 @@ func NewAdminService(
459460
entClient *dbent.Client,
460461
settingService *SettingService,
461462
defaultSubAssigner DefaultSubscriptionAssigner,
463+
userSubRepo UserSubscriptionRepository,
462464
) AdminService {
463465
return &adminServiceImpl{
464466
userRepo: userRepo,
@@ -476,6 +478,7 @@ func NewAdminService(
476478
entClient: entClient,
477479
settingService: settingService,
478480
defaultSubAssigner: defaultSubAssigner,
481+
userSubRepo: userSubRepo,
479482
}
480483
}
481484

@@ -1277,9 +1280,14 @@ func (s *adminServiceImpl) AdminUpdateAPIKeyGroupID(ctx context.Context, keyID i
12771280
if group.Status != StatusActive {
12781281
return nil, infraerrors.BadRequest("GROUP_NOT_ACTIVE", "target group is not active")
12791282
}
1280-
// 订阅类型分组:不允许通过此 API 直接绑定,需通过订阅管理流程
1283+
// 订阅类型分组:用户须持有该分组的有效订阅才可绑定
12811284
if group.IsSubscriptionType() {
1282-
return nil, infraerrors.BadRequest("SUBSCRIPTION_GROUP_NOT_ALLOWED", "subscription groups must be managed through the subscription workflow")
1285+
if _, err := s.userSubRepo.GetActiveByUserIDAndGroupID(ctx, apiKey.UserID, *groupID); err != nil {
1286+
if errors.Is(err, ErrSubscriptionNotFound) {
1287+
return nil, infraerrors.BadRequest("SUBSCRIPTION_REQUIRED", "user does not have an active subscription for this group")
1288+
}
1289+
return nil, err
1290+
}
12831291
}
12841292

12851293
gid := *groupID

frontend/src/components/admin/user/UserApiKeysModal.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ const load = async () => {
162162
const loadGroups = async () => {
163163
try {
164164
const groups = await adminAPI.groups.getAll()
165-
// 过滤掉订阅类型分组(需通过订阅管理流程绑定)
166-
allGroups.value = groups.filter((g) => g.subscription_type !== 'subscription')
165+
allGroups.value = groups
167166
} catch (error) {
168167
console.error('Failed to load groups:', error)
169168
}

0 commit comments

Comments
 (0)