From b8495eeeb37121acb77ce587b08e8861398eaf40 Mon Sep 17 00:00:00 2001 From: "CJACK." Date: Fri, 20 Mar 2026 23:34:29 +0800 Subject: [PATCH] surface account test config writeability and save failures --- internal/admin/handler_accounts_testing.go | 20 +++++++++++++++--- internal/deepseek/client_auth.go | 13 ++++++++++-- internal/deepseek/client_auth_refresh_test.go | 21 +++++++++++++++++++ 3 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 internal/deepseek/client_auth_refresh_test.go diff --git a/internal/admin/handler_accounts_testing.go b/internal/admin/handler_accounts_testing.go index 93b0c8d..e528de0 100644 --- a/internal/admin/handler_accounts_testing.go +++ b/internal/admin/handler_accounts_testing.go @@ -89,7 +89,15 @@ func runAccountTestsConcurrently(accounts []config.Account, maxConcurrency int, func (h *Handler) testAccount(ctx context.Context, acc config.Account, model, message string) map[string]any { start := time.Now() identifier := acc.Identifier() - result := map[string]any{"account": identifier, "success": false, "response_time": 0, "message": "", "model": model, "session_count": 0} + result := map[string]any{ + "account": identifier, + "success": false, + "response_time": 0, + "message": "", + "model": model, + "session_count": 0, + "config_writable": !h.Store.IsEnvBacked(), + } defer func() { status := "failed" if ok, _ := result["success"].(bool); ok { @@ -105,7 +113,10 @@ func (h *Handler) testAccount(ctx context.Context, acc config.Account, model, me return result } token = newToken - _ = h.Store.UpdateAccountToken(acc.Identifier(), token) + if err := h.Store.UpdateAccountToken(acc.Identifier(), token); err != nil { + result["message"] = "登录成功但写入配置失败: " + err.Error() + return result + } } authCtx := &authn.RequestAuth{UseConfigToken: false, DeepSeekToken: token} sessionID, err := h.DS.CreateSession(ctx, authCtx, 1) @@ -117,7 +128,10 @@ func (h *Handler) testAccount(ctx context.Context, acc config.Account, model, me } token = newToken authCtx.DeepSeekToken = token - _ = h.Store.UpdateAccountToken(acc.Identifier(), token) + if err := h.Store.UpdateAccountToken(acc.Identifier(), token); err != nil { + result["message"] = "刷新 token 成功但写入配置失败: " + err.Error() + return result + } sessionID, err = h.DS.CreateSession(ctx, authCtx, 1) if err != nil { result["message"] = "创建会话失败: " + err.Error() diff --git a/internal/deepseek/client_auth.go b/internal/deepseek/client_auth.go index bedb75e..00c72a1 100644 --- a/internal/deepseek/client_auth.go +++ b/internal/deepseek/client_auth.go @@ -73,7 +73,7 @@ func (c *Client) CreateSession(ctx context.Context, a *auth.RequestAuth, maxAtte } config.Logger.Warn("[create_session] failed", "status", status, "code", code, "biz_code", bizCode, "msg", msg, "biz_msg", bizMsg, "use_config_token", a.UseConfigToken, "account", a.AccountID) if a.UseConfigToken { - if isTokenInvalid(status, code, bizCode, msg, bizMsg) && !refreshed { + if !refreshed && shouldAttemptRefresh(status, code, bizCode, msg, bizMsg) { if c.Auth.RefreshToken(ctx, a) { refreshed = true continue @@ -118,7 +118,7 @@ func (c *Client) GetPow(ctx context.Context, a *auth.RequestAuth, maxAttempts in } config.Logger.Warn("[get_pow] failed", "status", status, "code", code, "biz_code", bizCode, "msg", msg, "biz_msg", bizMsg, "use_config_token", a.UseConfigToken, "account", a.AccountID) if a.UseConfigToken { - if isTokenInvalid(status, code, bizCode, msg, bizMsg) && !refreshed { + if !refreshed && shouldAttemptRefresh(status, code, bizCode, msg, bizMsg) { if c.Auth.RefreshToken(ctx, a) { refreshed = true continue @@ -160,6 +160,15 @@ func isTokenInvalid(status int, code int, bizCode int, msg string, bizMsg string strings.Contains(msg, "invalid jwt") } +func shouldAttemptRefresh(status int, code int, bizCode int, msg string, bizMsg string) bool { + if isTokenInvalid(status, code, bizCode, msg, bizMsg) { + return true + } + // Some DeepSeek failures come back as HTTP 200/code=0 but with non-zero biz_code. + // In managed-account mode this is commonly stale login state, so try one refresh. + return status == http.StatusOK && code == 0 && bizCode != 0 +} + func extractResponseStatus(resp map[string]any) (code int, bizCode int, msg string, bizMsg string) { code = intFrom(resp["code"]) msg, _ = resp["msg"].(string) diff --git a/internal/deepseek/client_auth_refresh_test.go b/internal/deepseek/client_auth_refresh_test.go new file mode 100644 index 0000000..b411bb7 --- /dev/null +++ b/internal/deepseek/client_auth_refresh_test.go @@ -0,0 +1,21 @@ +package deepseek + +import "testing" + +func TestShouldAttemptRefreshOnTokenInvalidSignal(t *testing.T) { + if !shouldAttemptRefresh(401, 0, 0, "unauthorized", "") { + t.Fatal("expected refresh when response indicates invalid token") + } +} + +func TestShouldAttemptRefreshOnBizCodeOnlyFailure(t *testing.T) { + if !shouldAttemptRefresh(200, 0, 400123, "", "session create failed") { + t.Fatal("expected refresh on non-zero biz_code with HTTP 200/code=0") + } +} + +func TestShouldAttemptRefreshFalseOnGenericServerError(t *testing.T) { + if shouldAttemptRefresh(500, 500, 0, "internal error", "") { + t.Fatal("did not expect refresh on generic server error") + } +}