diff --git a/backend/internal/handler/admin/account_handler.go b/backend/internal/handler/admin/account_handler.go
index f6082e09..98ead284 100644
--- a/backend/internal/handler/admin/account_handler.go
+++ b/backend/internal/handler/admin/account_handler.go
@@ -1122,6 +1122,14 @@ func (h *AccountHandler) BulkUpdate(c *gin.Context) {
SkipMixedChannelCheck: skipCheck,
})
if err != nil {
+ var mixedErr *service.MixedChannelError
+ if errors.As(err, &mixedErr) {
+ c.JSON(409, gin.H{
+ "error": "mixed_channel_warning",
+ "message": mixedErr.Error(),
+ })
+ return
+ }
response.ErrorFrom(c, err)
return
}
diff --git a/backend/internal/service/admin_service.go b/backend/internal/service/admin_service.go
index ee78b6d3..a4733fe6 100644
--- a/backend/internal/service/admin_service.go
+++ b/backend/internal/service/admin_service.go
@@ -1565,6 +1565,19 @@ func (s *adminServiceImpl) BulkUpdateAccounts(ctx context.Context, input *BulkUp
groupNameByID = loadedNames
}
+ // 预检查混合渠道风险:在任何写操作之前,若发现风险立即返回错误。
+ if needMixedChannelCheck {
+ for _, accountID := range input.AccountIDs {
+ platform := platformByID[accountID]
+ if platform == "" {
+ continue
+ }
+ if err := s.checkMixedChannelRisk(ctx, accountID, platform, *input.GroupIDs); err != nil {
+ return nil, err
+ }
+ }
+ }
+
if input.RateMultiplier != nil {
if *input.RateMultiplier < 0 {
return nil, errors.New("rate_multiplier must be >= 0")
@@ -1609,31 +1622,6 @@ func (s *adminServiceImpl) BulkUpdateAccounts(ctx context.Context, input *BulkUp
platform := ""
if input.GroupIDs != nil {
- // 检查混合渠道风险(除非用户已确认)
- if !input.SkipMixedChannelCheck {
- platform = platformByID[accountID]
- if platform == "" {
- account, err := s.accountRepo.GetByID(ctx, accountID)
- if err != nil {
- entry.Success = false
- entry.Error = err.Error()
- result.Failed++
- result.FailedIDs = append(result.FailedIDs, accountID)
- result.Results = append(result.Results, entry)
- continue
- }
- platform = account.Platform
- }
- if err := s.checkMixedChannelRiskWithPreloaded(accountID, platform, *input.GroupIDs, groupAccountsByID, groupNameByID); err != nil {
- entry.Success = false
- entry.Error = err.Error()
- result.Failed++
- result.FailedIDs = append(result.FailedIDs, accountID)
- result.Results = append(result.Results, entry)
- continue
- }
- }
-
if err := s.accountRepo.BindGroups(ctx, accountID, *input.GroupIDs); err != nil {
entry.Success = false
entry.Error = err.Error()
@@ -2541,6 +2529,6 @@ type MixedChannelError struct {
}
func (e *MixedChannelError) Error() string {
- return fmt.Sprintf("mixed_channel_warning: Group '%s' contains both %s and %s accounts. Using mixed channels in the same context may cause thinking block signature validation issues, which will fallback to non-thinking mode for historical messages.",
+ return fmt.Sprintf("警告:分组 \"%s\" 中同时包含 %s 和 %s 账号。混合使用不同渠道可能导致 thinking block 签名验证问题,会导致请求报错,请确认混合调用的 Anthropic 账号对应的是反重力反代的 Claude API。确定要继续吗?",
e.GroupName, e.CurrentPlatform, e.OtherPlatform)
}
diff --git a/frontend/src/components/account/BulkEditAccountModal.vue b/frontend/src/components/account/BulkEditAccountModal.vue
index ae16ff1a..38c07e69 100644
--- a/frontend/src/components/account/BulkEditAccountModal.vue
+++ b/frontend/src/components/account/BulkEditAccountModal.vue
@@ -756,6 +756,17 @@
+
+