feat: 顺运宝登录接入验证码自动识别 (#47)
#46 的登录只有手工输验证码一条路,而会话 24 小时就过期——每天第一次 同步都得有人在场,将来也做不了定时同步。 docs/admin/08 §8 当时写死"不引入 OCR 服务",理由是"多一个必须先启动的 东西"。那条判断基于示例脚本里的 http://127.0.0.1:8000/ocr(本机服务)。 用户提供了托管地址后前提不成立,本工单推翻它——文档里改写并保留原文, 让后来人知道这个决定变过、为什么变。 OCR 优先、手工兜底:识别成功直接登录,失败或服务不可达降级到 #46 已有的 手工弹窗,并在弹窗里说明是"已尝试 N 次"还是"服务不可用"。手工路径不删, 外部服务挂了不该让整个同步功能不可用。 识别失败也是 code:200。实测拿无文字图片探测 https://ocr.ilapage.cn/ocr 返回 {"code":200,"message":"Success","data":""}——不是错误码。所以 Recognize 只负责"这次 HTTP 调用有没有问题",空 data 照常返回 (", nil), 业务校验交给调用方;空 data 和长度不对收敛到同一个 len(code) != 4, 一条规则覆盖两种情况。 不合格的验证码不拿去登录:白费一次尝试,且频繁错误登录可能触发风控。 审查时变异测试发现这条没有测试守着——原测试只断言"重新取图了"和 "最终登录成功",禁用长度校验后依然成立。已补 loginRecorder 记录每次 提交到 /am/auth/login 的 code,断言登录只被调用一次且提交的是合格的那个。 每次重试重新取图(同一张图再识别结果一样,且可能已被上次失败的登录作废); OCR 用独立 HTTP 客户端不带顺运宝 Cookie;验证码图片只在内存里传,不落盘。 OCR 不可达立即降级、不占用重试次数——对着连不上的地址重试 5 次, 操作员要等 50 秒才看到手工输入框,结果注定一样。 测试全部用 httptest,不打真实的 ocr.ilapage.cn 和 shunyunbaoerp.com。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+12
-2
@@ -93,6 +93,16 @@ type SybConfig struct {
|
||||
PageSize int `yaml:"page_size"`
|
||||
MaxMatches int `yaml:"max_matches"`
|
||||
SyncFrom string `yaml:"sync_from"`
|
||||
|
||||
// OcrURL 是验证码自动识别服务的地址(工单 #47)。
|
||||
//
|
||||
// `[必须]` 留空 = 禁用自动识别,登录时直接走手工输入弹窗,不报错——
|
||||
// 这是有意的降级路径,见 docs/admin/08-顺运宝接口.md「验证码自动识别」一节。
|
||||
OcrURL string `yaml:"ocr_url"`
|
||||
|
||||
// OcrMaxAttempts 是自动识别失败后的重试次数上限,<=0 时代码里退化成 5
|
||||
// (示例脚本 raw_data/shunyunbaoerp_single.py 用的也是 5)。
|
||||
OcrMaxAttempts int `yaml:"ocr_max_attempts"`
|
||||
}
|
||||
|
||||
// String 把密码打码,防止 %v、log.Printf("%+v", cfg) 这类写法
|
||||
@@ -106,8 +116,8 @@ func (c SybConfig) String() string {
|
||||
pw = "****"
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"SybConfig{BaseURL:%s Username:%s Password:%s PageSize:%d MaxMatches:%d SyncFrom:%s}",
|
||||
c.BaseURL, c.Username, pw, c.PageSize, c.MaxMatches, c.SyncFrom)
|
||||
"SybConfig{BaseURL:%s Username:%s Password:%s PageSize:%d MaxMatches:%d SyncFrom:%s OcrURL:%s OcrMaxAttempts:%d}",
|
||||
c.BaseURL, c.Username, pw, c.PageSize, c.MaxMatches, c.SyncFrom, c.OcrURL, c.OcrMaxAttempts)
|
||||
}
|
||||
|
||||
// Config 是 config.yaml 的顶层结构。目前只有顺运宝一节,
|
||||
|
||||
Reference in New Issue
Block a user