diff --git a/admin/handler/web/inner_code.go b/admin/handler/web/inner_code.go index 255243e..3ef8e80 100644 --- a/admin/handler/web/inner_code.go +++ b/admin/handler/web/inner_code.go @@ -146,11 +146,11 @@ func (h *Handler) InnerCodeMatch(c *gin.Context) { h.innerCodeRedirect(c, businessDate, status, keyword, pageNumber, "顺运宝地址配置有误,没有执行匹配。") return } - if err := service.EnsureSybSession(h.db, client, cfg.Syb.Username, time.Now()); err != nil { - message := "校验顺运宝会话失败,没有执行匹配。" - if errors.Is(err, service.ErrSybLoginRequired) { - message = "顺运宝会话已过期,请先到“顺运宝数据”页面登录后再匹配。" - } + autoLoggedIn, message := ensureInnerCodeMatchSession( + func() error { return service.EnsureSybSession(h.db, client, cfg.Syb.Username, time.Now()) }, + func() (string, bool) { return h.attemptSybAutoLogin(c, client, *cfg) }, + ) + if message != "" { h.innerCodeRedirect(c, businessDate, status, keyword, pageNumber, message) return } @@ -159,11 +159,35 @@ func (h *Handler) InnerCodeMatch(c *gin.Context) { h.innerCodeRedirect(c, businessDate, status, keyword, pageNumber, "匹配失败:"+err.Error()) return } - message := fmt.Sprintf("匹配完成:处理 %d 条,可回写 %d 条,已存在 %d 条,跳过 %d 条,失败 %d 条。", + message = fmt.Sprintf("匹配完成:处理 %d 条,可回写 %d 条,已存在 %d 条,跳过 %d 条,失败 %d 条。", result.Total, result.Ready, result.AlreadyFilled, result.Skipped, result.Failed) + if autoLoggedIn { + message = "顺运宝自动登录成功;" + message + } h.innerCodeRedirect(c, businessDate, status, keyword, pageNumber, message) } +// ensureInnerCodeMatchSession 只负责“检查缓存 → 必要时自动登录”的编排。 +// 自动登录成功后调用方继续使用同一个 client;这里绝不启动顺运宝同步。 +func ensureInnerCodeMatchSession(checkSession func() error, autoLogin func() (string, bool)) (bool, string) { + err := checkSession() + if err == nil { + return false, "" + } + if !errors.Is(err, service.ErrSybLoginRequired) { + return false, "校验顺运宝会话失败,没有执行匹配。" + } + reason, ok := autoLogin() + if ok { + return true, "" + } + reason = strings.TrimSpace(reason) + if reason == "" { + reason = "未配置验证码识别服务,无法自动登录" + } + return false, "顺运宝自动登录未完成:" + reason + ";没有执行匹配,请到“顺运宝数据”页面完成登录。" +} + // InnerCodeApply 在操作员确认后逐条执行安全回写。 func (h *Handler) InnerCodeApply(c *gin.Context) { businessDate, status, keyword := c.PostForm("date"), c.PostForm("status"), c.PostForm("q") diff --git a/admin/handler/web/inner_code_test.go b/admin/handler/web/inner_code_test.go index bb71c91..1a4c7b5 100644 --- a/admin/handler/web/inner_code_test.go +++ b/admin/handler/web/inner_code_test.go @@ -1,6 +1,12 @@ package web -import "testing" +import ( + "errors" + "strings" + "testing" + + "cmautobuy/admin/service" +) func TestParseInnerCodeIDs_过滤无效并保持首次选择顺序(t *testing.T) { got := parseInnerCodeIDs([]string{" 8 ", "bad", "0", "8", "3"}) @@ -8,3 +14,57 @@ func TestParseInnerCodeIDs_过滤无效并保持首次选择顺序(t *testing.T) t.Fatalf("解析结果不正确: %v", got) } } + +func TestEnsureInnerCodeMatchSession_有效会话不自动登录(t *testing.T) { + loginCalls := 0 + autoLoggedIn, message := ensureInnerCodeMatchSession( + func() error { return nil }, + func() (string, bool) { loginCalls++; return "", true }, + ) + if autoLoggedIn || message != "" || loginCalls != 0 { + t.Fatalf("有效会话不应自动登录: auto=%v message=%q calls=%d", autoLoggedIn, message, loginCalls) + } +} + +func TestEnsureInnerCodeMatchSession_过期后自动登录成功(t *testing.T) { + loginCalls := 0 + autoLoggedIn, message := ensureInnerCodeMatchSession( + func() error { return service.ErrSybLoginRequired }, + func() (string, bool) { loginCalls++; return "", true }, + ) + if !autoLoggedIn || message != "" || loginCalls != 1 { + t.Fatalf("过期会话应自动登录一次: auto=%v message=%q calls=%d", autoLoggedIn, message, loginCalls) + } +} + +func TestEnsureInnerCodeMatchSession_自动登录失败给出恢复路径(t *testing.T) { + autoLoggedIn, message := ensureInnerCodeMatchSession( + func() error { return service.ErrSybLoginRequired }, + func() (string, bool) { return "验证码识别服务不可达", false }, + ) + if autoLoggedIn || !strings.Contains(message, "验证码识别服务不可达") || + !strings.Contains(message, "顺运宝数据") || !strings.Contains(message, "没有执行匹配") { + t.Fatalf("自动登录失败提示不完整: auto=%v message=%q", autoLoggedIn, message) + } +} + +func TestEnsureInnerCodeMatchSession_未配置OCR给出明确原因(t *testing.T) { + _, message := ensureInnerCodeMatchSession( + func() error { return service.ErrSybLoginRequired }, + func() (string, bool) { return "", false }, + ) + if !strings.Contains(message, "未配置验证码识别服务") { + t.Fatalf("未配置 OCR 的提示不明确: %q", message) + } +} + +func TestEnsureInnerCodeMatchSession_普通错误不尝试登录(t *testing.T) { + loginCalls := 0 + autoLoggedIn, message := ensureInnerCodeMatchSession( + func() error { return errors.New("database unavailable") }, + func() (string, bool) { loginCalls++; return "", true }, + ) + if autoLoggedIn || message != "校验顺运宝会话失败,没有执行匹配。" || loginCalls != 0 { + t.Fatalf("普通错误不应自动登录: auto=%v message=%q calls=%d", autoLoggedIn, message, loginCalls) + } +}