iotlcm-worker-svr:异步执行中枢(RabbitMQ 消费者 + 全量同步编排)。 - Go module:iotlcm-worker-svr → git.5iot.com/iotlcm-public/iotlcm-worker-svr(214 文件) - 凭据与内部标识脱敏:MySQL/Redis 凭据、私网与生产 IP、内网域名、 库名 xiujie_admin → iotlcm、哨兵名占位 - 159 处真实手机号 → 合法 11 位编造号(保留号段与后 4 位,维持互异, 不破坏「校验 MSISDN 格式」等测试断言) - 移除内部资产:CI 触发器、Jenkins 触发测试文档、预演门禁与镜像 digest 记录 - Dockerfile 改用公共基础镜像(golang:1.25-alpine → alpine:3.23), 移除写死的 git 凭据 - README 重写(架构/全量同步模型/配置/已知失败),补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes 已知:3 个测试包在源仓同一提交上同样失败(client/upstream、 service/allocate、service/inbound),属既有缺陷,本仓未引入亦未修复。
5.0 KiB
Full-sync HTTP control API
The handler imports no queue or storage implementations. Inject the five
contract interfaces in Dependencies: RunLister, RunReader,
FailurePager, ReplayRequester, and ActionReader. The replay service owns durable
idempotency and execution; the HTTP layer does not cache requests or launch work.
Use NewRunReader(live, archive, rounds) to compose LiveRunReader,
ArchivedRunReader, and RoundStatsReader into a RunReader. Live metadata
wins and returns source: "live"; only ErrNotFound from live metadata permits
archive fallback, returning source: "archive". Live storage errors and round
lookup errors propagate. Archive and round readers may be nil to disable those
lookups. A failure listing always calls FailurePager directly, never archives.
Routes and responses
All routes require Authorization: Bearer <configured token>. Missing, wrong,
or ambiguous credentials return 401 with a WWW-Authenticate challenge. An
empty configured token is a startup configuration error. Responses use JSON and
Cache-Control: no-store.
| Method | Route | Success body |
|---|---|---|
| GET | /internal/v1/full-sync-runs?syncKind=&limit= |
200 contract.RunList |
| GET | /internal/v1/full-sync-runs/{runId} |
200 contract.RunView |
| GET | /internal/v1/full-sync-runs/{runId}/failures?cursor=&limit= |
200 contract.FailurePage |
| POST | /internal/v1/full-sync-runs/{runId}/replay |
202 contract.ReplayAccepted |
| GET | /internal/v1/full-sync-actions/{actionId} |
200 contract.ReplayAction |
Run list results merge active Redis metadata with flat run-<runId>.json
archives under the configured archive root. Active Redis metadata wins when an
ID exists in both sources. syncKind accepts traffic_usage or sim_status;
limit defaults to 50 and is capped at 100.
Failure limits use the contract rules: absent, empty, or zero selects 100;
positive values up to 500 are accepted; other values are rejected. Repeated
cursor/limit parameters and malformed query encoding are rejected. Cursors are
opaque and passed unchanged after URL decoding. The pager validates cursor
contents and run binding. Empty pages serialize items: [] and preserve
nextCursor; only an empty next cursor signals completion.
Replay requires a single Idempotency-Key header and a JSON body such as
{"requestedBy":"operator"}. The body is limited to 64 KiB, must contain one
JSON object with no unknown fields, and must satisfy ReplayRequest.Validate.
If supplied, Content-Type must be application/json (parameters are allowed).
The exact requestedBy value is preserved for fingerprinting. Repeated accepted
requests return the service's existing action/target and current status with
202. The adapter constructs statusUrl and the Location header from actionId.
All application errors use this envelope, without storage error details:
{"error":{"code":"invalid_argument","message":"Invalid request"}}
| Condition | HTTP status | Error code |
|---|---|---|
Invalid path, query, header, body, or ErrInvalidArgument |
400 | invalid_argument |
| Missing/wrong bearer credentials | 401 | unauthorized |
Unknown route/resource or ErrNotFound |
404 | not_found |
| Unsupported route method | 405 | method_not_allowed |
ErrConflict (including changed idempotent request), ErrLeaseLost |
409 | conflict |
ErrFailureDetailsUnavailable |
410 | failure_details_unavailable |
| Oversized replay body | 413 | body_too_large |
ErrBusy |
503 | busy |
| Other service errors or unencodable service output | 500 | internal_error |
Lifecycle
NewHandler(token, deps) can be mounted by an existing HTTP server. For an
independent listener, construct NewServer(Config{Addr: address, BearerToken: token}, deps) and call Start(workerCtx). Start binds synchronously and
returns address-in-use and other startup errors directly. Addr() exposes the
bound address, including an OS-assigned port when configured with port zero.
Call Wait() after Start, normally in the worker's supervision goroutine. It
returns serving or shutdown failures; it also returns startup failures if Start
failed. The server is single-use and rejects a second Start. Wait can have
multiple callers but must not be called before Start.
Canceling workerCtx closes the listener and drains active HTTP requests.
Active request contexts stay usable during the drain. ShutdownTimeout defaults
to ten seconds; if it expires, active connections are closed and Wait returns
an error wrapping context.DeadlineExceeded. Normal cancellation with a
successful drain returns nil. No worker wiring or configuration-file changes
are part of this package.
Verification
gofmt -l internal/fullsync/httpapi
go test ./internal/fullsync/...
go test ./internal/fullsync/httpapi -cover
go vet ./internal/fullsync/...
Tests use contract mocks and local HTTP listeners, requiring no Redis or archive
files. go test -race ./internal/fullsync/... additionally requires a supported
C compiler and CGO enabled.