Admin:顺运宝登录接入验证码自动识别 #47

Closed
opened 2026-08-09 12:18:54 +08:00 by ila · 1 comment
Owner

基本信息

  • 状态:已完成
  • 类型:需求(体验改进)
  • 父级大工单:#14
  • 所属 MVP / 版本:#15 / MVP
  • 前置:#46(顺运宝同步,已合入)
  • 相关文档:08 顺运宝接口

要解决什么

#46 的登录只有手工输验证码一条路。顺运宝会话24 小时就过期,
意味着每天第一次同步都必须有人在场,而且将来做不了定时同步。

docs/admin/08 §8 当时写死了「不引入 OCR 服务」,理由是
「多一个必须先启动的东西」——那条判断基于示例脚本里的
http://127.0.0.1:8000/ocr(本机服务)。

用户提供了托管地址 https://ocr.ilapage.cn/ocr,那条理由不成立了:
没有要启动的东西,就是一次 HTTP 调用。本工单推翻 08 §8 的这条决定。

做什么 / 不做什么

做:

  1. 登录时先调 OCR,识别成功就自动登录,全程无人值守
  2. OCR 识别错/不可达时降级到现有的手工输入弹窗
  3. OCR 地址、重试次数走 config.yaml,留空则完全禁用
  4. 更新 08 §8 那条已被推翻的决定

不做:

  • 不删现有的手工输入弹窗——它是兜底路径,必须保留
  • 不做定时/自动同步(本工单只是让它成为可能)
  • 不改会话缓存、同步逻辑、字段映射

怎么做

响应格式(已实测)

POST https://ocr.ilapage.cn/ocr
Content-Type: multipart/form-data,字段名 file
→ 200 {"code":200,"message":"Success","data":"kycv"}

实测耗时约 1.4 秒。

[必须] 识别失败也是 code:200,这是最容易写错的地方

实测用一张无文字的图片探测,返回:

{"code":200,"message":"Success","data":""}

识别不出来不是错误码,是 code:200 加空 data。

[必须] 判断成功必须同时满足:HTTP 200、code == 200、data 非空。
只看 code 会把「没识别出来」当成功,拿空字符串去登录——白白消耗一次
登录尝试,还可能触发对方风控。

[必须] 长度校验:不是 4 位就不要拿去登录

// 顺运宝验证码固定 4 位字母数字(08 §3.2 实测)。
// 长度不对说明识别错了,直接换一张重试,不要拿去试登录——
// 白费一次尝试,而且频繁的错误登录可能触发风控。
if len(code) != 4 { /* 换图重试 */ }

[必须] 只保留字母和数字,滤掉 OCR 可能带回的空格和标点
(示例脚本 ocr_captcha() 的做法)。

重试与降级

点同步 → 会话过期
  ├─ OCR 已配置
  │   └─ 循环 N 次:取验证码图 → OCR → 校验(非空 && 4位) → 登录
  │        ├─ 登录成功 ─────────────────→ 直接同步,无人值守
  │        └─ N 次都失败 ───────────────┐
  └─ OCR 未配置 / 服务不可达 ────────────┴─→ 弹手工输入框(#46 已有)

[必须] OCR 不可达要降级,不是报错。 外部服务挂了不该让整个同步
功能不可用——手工路径一直在,走它就是了。

[必须] 每次重试都要重新取验证码图。同一张图再识别一次结果一样,
纯属浪费;而且验证码可能已经被上一次失败的登录作废了。

[必须] 重试次数上限可配,默认 5(示例脚本用的也是 5)。

[必须] 降级到手工时,弹窗里要说明为什么:
「自动识别验证码失败(已尝试 5 次),请手工输入」或
「验证码识别服务暂时不可用,请手工输入」。不写原因的话,
操作员不知道是配置错了还是服务挂了。

配置

syb:
  # 验证码自动识别。留空则只用手工输入。
  ocr_url: https://ocr.ilapage.cn/ocr
  ocr_max_attempts: 5

[必须] ocr_url 留空 = 禁用,直接走手工,不报错。

[必须] OCR 请求要有独立的超时(实测 1.4 秒,[建议] 设 10 秒),
不要和顺运宝的超时共用——OCR 慢不该拖垮整个登录流程。

[必须] 调 OCR 不要带顺运宝的 Cookie。用独立的 HTTP 客户端,
理由和示例脚本注释里写的一样:避免把 ERP 会话泄漏给另一个服务。

验证码图片不落盘

[必须] 示例脚本把图片存成 captcha.jpg,那是命令行脚本的做法。
Admin 里在内存里传字节即可,不要写文件——省掉清理逻辑,
也不会在 data/ 里堆垃圾。

文档要改

[必须] docs/admin/08 §8 里那条「不引入 OCR 服务」已被本工单推翻,
要改写并说明原委(原判断基于本机服务的前提,托管服务不适用),
不要直接删掉——留着能让后来人知道这个决定变过、为什么变。

[必须] 补一条说明:验证码图片会被发送到外部 OCR 服务。
当前那个域名是自己的服务,所以不算交给第三方;换成别人的服务时
必须重新评估。

预计修改文件

文件 改什么
admin/config/config.go OcrURL / OcrMaxAttempts
admin/config.example.yaml 两个新配置项 + 注释
admin/syb/ocr.go 新建:OCR 客户端
admin/syb/ocr_test.go 新建:用 httptest 假 OCR
admin/syb/client.go 登录流程接入 OCR 重试
admin/service/syb.go 降级判断、弹窗提示文案
admin/templates/syb/list.html 弹窗显示降级原因
docs/admin/08-顺运宝接口.md §8 改写;新增 OCR 一节
docs/admin/00-getting-started.md 配置说明

验收标准

OCR 调用

  • 成功识别(code:200 + data 非空 + 4 位)→ 自动登录,不弹手工框
  • code:200 但 data 为空 → 判定失败并重试,不拿空串去登录
  • 识别结果不是 4 位 → 换图重试,不拿去登录
  • 结果里的空格/标点被滤掉
  • 每次重试重新取验证码图
  • 达到 ocr_max_attempts 仍失败 → 降级手工,弹窗说明「已尝试 N 次」

降级

  • ocr_url 留空 → 直接走手工,不报错
  • OCR 服务超时/连不上 → 降级手工,弹窗说明服务不可用
  • OCR 返回非 JSON / 结构异常 → 降级手工,不 panic

其他

  • OCR 请求不携带顺运宝 Cookie(独立客户端)
  • OCR 有独立超时
  • 验证码图片不写文件
  • 手工输入弹窗仍然可用(#46 的路径未破坏)
  • 测试用 httptest,不打真实的 ocr.ilapage.cn 和 shunyunbaoerp.com
  • docs/admin/08 §8 那条决定已改写并说明原委,不是删掉
  • 文档写明图片会发到外部服务
  • 五个页面均 200
  • GOTOOLCHAIN=go1.23.0 下 go vet / gofmt -l . / go test ./... 全过

怎么验证

cd D:\chengma\cmautobuy\admin
$env:GOTOOLCHAIN="go1.23.0"
go vet ./...; gofmt -l .; go test ./... -count=1
Remove-Item Env:GOTOOLCHAIN

[必须] 单元测试要覆盖这三种假响应:

{"code":200,"message":"Success","data":"kycv"}   → 成功
{"code":200,"message":"Success","data":""}       → 失败并重试
{"code":200,"message":"Success","data":"ab"}     → 长度不对,重试

真机验证(需要真实账号和网络,实现方跑不了就如实写进「未验证到的部分」):

  1. go run . → /syb → 点同步 → 不应弹验证码框,直接开始同步
  2. 把 ocr_url 改成一个不存在的地址 → 再点同步 → 应弹手工框并说明服务不可用
  3. ocr_url 留空 → 再点同步 → 应直接弹手工框,不报错

风险和回退

风险 应对
data 为空当成识别成功 已列为验收项,实测证据在「怎么做」里
OCR 挂了导致同步完���不可用 强制降级到手工,已列为验收项
错误的验证码反复登录触发风控 长度校验 + 重试上限;不合格的不拿去登录
把顺运宝 Cookie 泄漏给 OCR 服务 独立 HTTP 客户端,已列为验收项
测试打真实服务 强制 httptest,已列为验收项

回退:git revert。手工输入路径不受影响,回退后仍能正常登录同步。

## 基本信息 - 状态:已完成 - 类型:需求(体验改进) - 父级大工单:#14 - 所属 MVP / 版本:#15 / MVP - 前置:**#46**(顺运宝同步,已合入) - 相关文档:[08 顺运宝接口](../docs/admin/08-顺运宝接口.md) ## 要解决什么 #46 的登录只有手工输验证码一条路。顺运宝会话**24 小时就过期**, 意味着**每天第一次同步都必须有人在场**,而且将来做不了定时同步。 `docs/admin/08` §8 当时写死了「不引入 OCR 服务」,理由是 「多一个必须先启动的东西」——那条判断基于示例脚本里的 `http://127.0.0.1:8000/ocr`(本机服务)。 **用户提供了托管地址 `https://ocr.ilapage.cn/ocr`,那条理由不成立了**: 没有要启动的东西,就是一次 HTTP 调用。本工单推翻 08 §8 的这条决定。 ## 做什么 / 不做什么 做: 1. 登录时**先调 OCR**,识别成功就自动登录,全程无人值守 2. OCR 识别错/不可达时**降级到现有的手工输入弹窗** 3. OCR 地址、重试次数走 `config.yaml`,留空则完全禁用 4. 更新 08 §8 那条已被推翻的决定 不做: - **不删现有的手工输入弹窗**——它是兜底路径,必须保留 - 不做定时/自动同步(本工单只是让它成为可能) - 不改会话缓存、同步逻辑、字段映射 ## 怎么做 ### 响应格式(已实测) ``` POST https://ocr.ilapage.cn/ocr Content-Type: multipart/form-data,字段名 file → 200 {"code":200,"message":"Success","data":"kycv"} ``` 实测耗时约 1.4 秒。 ### `[必须]` 识别失败也是 `code:200`,这是最容易写错的地方 实测用一张无文字的图片探测,返回: ```json {"code":200,"message":"Success","data":""} ``` **识别不出来不是错误码,是 `code:200` 加空 `data`。** `[必须]` 判断成功必须同时满足:HTTP 200、`code == 200`、**`data` 非空**。 只看 `code` 会把「没识别出来」当成功,拿空字符串去登录——白白消耗一次 登录尝试,还可能触发对方风控。 ### `[必须]` 长度校验:不是 4 位就不要拿去登录 ```go // 顺运宝验证码固定 4 位字母数字(08 §3.2 实测)。 // 长度不对说明识别错了,直接换一张重试,不要拿去试登录—— // 白费一次尝试,而且频繁的错误登录可能触发风控。 if len(code) != 4 { /* 换图重试 */ } ``` `[必须]` 只保留字母和数字,滤掉 OCR 可能带回的空格和标点 (示例脚本 `ocr_captcha()` 的做法)。 ### 重试与降级 ```text 点同步 → 会话过期 ├─ OCR 已配置 │ └─ 循环 N 次:取验证码图 → OCR → 校验(非空 && 4位) → 登录 │ ├─ 登录成功 ─────────────────→ 直接同步,无人值守 │ └─ N 次都失败 ───────────────┐ └─ OCR 未配置 / 服务不可达 ────────────┴─→ 弹手工输入框(#46 已有) ``` `[必须]` **OCR 不可达要降级,不是报错。** 外部服务挂了不该让整个同步 功能不可用——手工路径一直在,走它就是了。 `[必须]` 每次重试都要**重新取验证码图**。同一张图再识别一次结果一样, 纯属浪费;而且验证码可能已经被上一次失败的登录作废了。 `[必须]` 重试次数上限可配,默认 **5**(示例脚本用的也是 5)。 `[必须]` 降级到手工时,弹窗里要说明**为什么**: 「自动识别验证码失败(已尝试 5 次),请手工输入」或 「验证码识别服务暂时不可用,请手工输入」。不写原因的话, 操作员不知道是配置错了还是服务挂了。 ### 配置 ```yaml syb: # 验证码自动识别。留空则只用手工输入。 ocr_url: https://ocr.ilapage.cn/ocr ocr_max_attempts: 5 ``` `[必须]` `ocr_url` 留空 = 禁用,直接走手工,**不报错**。 `[必须]` OCR 请求要有**独立的超时**(实测 1.4 秒,`[建议]` 设 10 秒), 不要和顺运宝的超时共用——OCR 慢不该拖垮整个登录流程。 `[必须]` 调 OCR **不要带顺运宝的 Cookie**。用独立的 HTTP 客户端, 理由和示例脚本注释里写的一样:避免把 ERP 会话泄漏给另一个服务。 ### 验证码图片不落盘 `[必须]` 示例脚本把图片存成 `captcha.jpg`,那是命令行脚本的做法。 Admin 里**在内存里传字节即可,不要写文件**——省掉清理逻辑, 也不会在 `data/` 里堆垃圾。 ### 文档要改 `[必须]` `docs/admin/08` §8 里那条「不引入 OCR 服务」**已被本工单推翻**, 要改写并说明原委(原判断基于本机服务的前提,托管服务不适用), **不要直接删掉**——留着能让后来人知道这个决定变过、为什么变。 `[必须]` 补一条说明:**验证码图片会被发送到外部 OCR 服务**。 当前那个域名是自己的服务,所以不算交给第三方;换成别人的服务时 必须重新评估。 ## 预计修改文件 | 文件 | 改什么 | |---|---| | `admin/config/config.go` | `OcrURL` / `OcrMaxAttempts` | | `admin/config.example.yaml` | 两个新配置项 + 注释 | | `admin/syb/ocr.go` | 新建:OCR 客户端 | | `admin/syb/ocr_test.go` | 新建:**用 `httptest` 假 OCR** | | `admin/syb/client.go` | 登录流程接入 OCR 重试 | | `admin/service/syb.go` | 降级判断、弹窗提示文案 | | `admin/templates/syb/list.html` | 弹窗显示降级原因 | | `docs/admin/08-顺运宝接口.md` | §8 改写;新增 OCR 一节 | | `docs/admin/00-getting-started.md` | 配置说明 | ## 验收标准 **OCR 调用** - [x] 成功识别(`code:200` + `data` 非空 + 4 位)→ 自动登录,不弹手工框 - [x] **`code:200` 但 `data` 为空 → 判定失败并重试**,不拿空串去登录 - [x] 识别结果不是 4 位 → 换图重试,不拿去登录 - [x] 结果里的空格/标点被滤掉 - [x] 每次重试**重新取验证码图** - [x] 达到 `ocr_max_attempts` 仍失败 → 降级手工,弹窗说明「已尝试 N 次」 **降级** - [x] `ocr_url` 留空 → 直接走手工,不报错 - [x] OCR 服务超时/连不上 → 降级手工,弹窗说明服务不可用 - [x] OCR 返回非 JSON / 结构异常 → 降级手工,不 panic **其他** - [x] OCR 请求不携带顺运宝 Cookie(独立客户端) - [x] OCR 有独立超时 - [x] 验证码图片不写文件 - [x] 手工输入弹窗仍然可用(#46 的路径未破坏) - [x] 测试用 `httptest`,**不打真实的 `ocr.ilapage.cn` 和 `shunyunbaoerp.com`** - [x] `docs/admin/08` §8 那条决定已改写并说明原委,不是删掉 - [x] 文档写明图片会发到外部服务 - [x] 五个页面均 200 - [x] `GOTOOLCHAIN=go1.23.0` 下 `go vet` / `gofmt -l .` / `go test ./...` 全过 ## 怎么验证 ```powershell cd D:\chengma\cmautobuy\admin $env:GOTOOLCHAIN="go1.23.0" go vet ./...; gofmt -l .; go test ./... -count=1 Remove-Item Env:GOTOOLCHAIN ``` `[必须]` 单元测试要覆盖这三种假响应: ```json {"code":200,"message":"Success","data":"kycv"} → 成功 {"code":200,"message":"Success","data":""} → 失败并重试 {"code":200,"message":"Success","data":"ab"} → 长度不对,重试 ``` **真机验证**(需要真实账号和网络,实现方跑不了就如实写进「未验证到的部分」): 1. `go run .` → `/syb` → 点同步 → **不应弹验证码框,直接开始同步** 2. 把 `ocr_url` 改成一个不存在的地址 → 再点同步 → **应弹手工框并说明服务不可用** 3. `ocr_url` 留空 → 再点同步 → **应直接弹手工框,不报错** ## 风险和回退 | 风险 | 应对 | |---|---| | **`data` 为空当成识别成功** | 已列为验收项,实测证据在「怎么做」里 | | OCR 挂了导致同步完���不可用 | 强制降级到手工,已列为验收项 | | 错误的验证码反复登录触发风控 | 长度校验 + 重试上限;不合格的不拿去登录 | | 把顺运宝 Cookie 泄漏给 OCR 服务 | 独立 HTTP 客户端,已列为验收项 | | 测试打真实服务 | 强制 `httptest`,已列为验收项 | 回退:`git revert`。手工输入路径不受影响,回退后仍能正常登录同步。
Author
Owner

用户已于 2026-08-09 明确通过验收,本工单验收完成。

  • 实现提交:2e686b1
  • 归档提交:2756999
  • 归档:docs/task/47-顺运宝验证码自动识别.md
  • 当前回归:Go 1.23.0 go build ./...、go test ./... -count=1、go vet ./... 全部通过。
  • 未重跑:真实顺运宝账号及真实 OCR 服务;该限制已写入归档,用户已明确接受。

现关闭工单,并同步 #15、#14 的任务清单。

用户已于 2026-08-09 明确通过验收,本工单验收完成。 - 实现提交:`2e686b1` - 归档提交:`2756999` - 归档:`docs/task/47-顺运宝验证码自动识别.md` - 当前回归:Go 1.23.0 `go build ./...`、`go test ./... -count=1`、`go vet ./...` 全部通过。 - 未重跑:真实顺运宝账号及真实 OCR 服务;该限制已写入归档,用户已明确接受。 现关闭工单,并同步 #15、#14 的任务清单。
ila closed this issue 2026-08-09 21:23:30 +08:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: chengma/cmautobuy#47