diff --git a/tools/bypos-collector/README.md b/tools/bypos-collector/README.md index 6039816..34c5b8d 100644 --- a/tools/bypos-collector/README.md +++ b/tools/bypos-collector/README.md @@ -58,9 +58,10 @@ GET http://zc.bypos.net/byGoodsService/byMessage.asmx/GetGoodsinfo ## 配置 -- `sdogid`:中心库账号 id(本项目所属云店账号的授权 id)。默认值见 - `collect.go` 的 `defaultSdogID`,也可用 `-sdogid` 参数或控制台覆盖。 - **这是账号级凭证**——若本仓库对外公开,建议改为从环境变量/外部配置读取。 +- `sdogid`:中心库账号 id(本项目所属云店账号的授权 id)。 + 优先从环境变量 `BYPOS_SDOGID` 读取,其次是命令行 `-sdogid` 参数, + 也可以在 Web 控制台的输入框中填写。**三者都未设时启动会警告、开始采集时会报错。** +- 网络错误自动重试(最多 3 次,指数退避 500ms/1s/2s)。 ## 导入 goods/天工库 diff --git a/tools/bypos-collector/collect.go b/tools/bypos-collector/collect.go index ebe6672..7ecb619 100644 --- a/tools/bypos-collector/collect.go +++ b/tools/bypos-collector/collect.go @@ -20,12 +20,21 @@ import ( ) // ---- upstream config ---- -// sdogid is the 云店 account license id observed in the live request. It is -// configurable so the tool is not tied to a single account. -const defaultSdogID = "137966" const endpoint = "http://zc.bypos.net/byGoodsService/byMessage.asmx/GetGoodsinfo" +// maxRetries is the number of retry attempts for transient network errors. +const maxRetries = 3 + +// resolveSdogID reads the account id from $BYPOS_SDOGID, falling back to the +// explicit argument (CLI flag or Web UI input). Returns empty if neither set. +func resolveSdogID(explicit string) string { + if v := os.Getenv("BYPOS_SDOGID"); v != "" { + return v + } + return explicit +} + var stringTagRe = regexp.MustCompile(`(?s)]*>(.*)`) // Product is the normalized record we persist (one JSON object per line). @@ -87,8 +96,8 @@ func ean13Check(body string) (string, bool) { return body + strconv.Itoa(chk), true } -// lookup queries the upstream central library for one barcode. -func (c *Collector) lookup(ctx context.Context, barcode string) (*Product, error) { +// lookupOnce performs a single HTTP request to the upstream central library. +func (c *Collector) lookupOnce(ctx context.Context, barcode string) (*Product, error) { tsMs := strconv.FormatInt(time.Now().Unix()*1000, 10) // always ends in 000 sparm1 := md5hex(c.sdogID) sparm2 := md5hex(barcode + tsMs) @@ -147,6 +156,30 @@ func (c *Collector) lookup(ctx context.Context, barcode string) (*Product, error return p, nil } +// lookup queries the upstream with up to maxRetries retries on transient errors. +func (c *Collector) lookup(ctx context.Context, barcode string) (*Product, error) { + var lastErr error + for attempt := 0; attempt <= maxRetries; attempt++ { + if ctx.Err() != nil { + return nil, ctx.Err() + } + p, err := c.lookupOnce(ctx, barcode) + if err == nil { + return p, nil + } + lastErr = err + if attempt < maxRetries { + backoff := time.Duration(1<

② 采集参数

+
+ + +
@@ -165,6 +169,7 @@ async function start(){ start_body: document.getElementById('start').value.trim(), end_body: document.getElementById('end').value.trim(), list: document.getElementById('list').value, + sdog_id: document.getElementById('sdogid').value.trim(), concurrency: parseInt(document.getElementById('concurrency').value)||3, delay_ms: parseInt(document.getElementById('delay').value)||0, log_miss: document.getElementById('logmiss').checked,