From 97948e2ca6ee30e1caf95069b89402e89439871f Mon Sep 17 00:00:00 2001 From: yml2213 Date: Tue, 6 Oct 2026 19:59:14 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=AE=B0=E5=BD=95=20P2=20=E5=A4=84?= =?UTF-8?q?=E7=90=86=E7=BB=93=E6=9E=9C=E5=B9=B6=E8=A1=A5=E5=85=85=20TZ=20?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 项目分析补充第三轮(P2)处理结果与验证方式,含时区改动的风险说明 - README 环境变量表补充 TZ:业务时间按北京时间处理,且 pgx 会把 timestamptz 解码到进程时区,要求显式设为 Asia/Shanghai - 遗留项收敛为:前端公共 chunk 体积、裸 time.Time 字段的 JSON 精度未对齐秒级约定 --- README.md | 1 + docs/项目分析.md | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 91b55a1..d18adff 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ make docker-down # 停止并移除 | `COMPOSE_PROJECT_NAME` | Docker Compose 项目名 | `affiliate_dash` | | `POSTGRES_DATA_VOLUME` | PostgreSQL 数据卷名 | `affiliate_dash_postgres_data` | | `GIN_MODE` | Gin 运行模式 | `debug` | +| `TZ` | 进程时区。**必须为 `Asia/Shanghai`**:业务时间统一按北京时间处理,且 pgx 会把 `timestamptz` 解码到进程时区。不一致时启动日志会打印 `[CONFIG] 时区提示` | `Asia/Shanghai` | | `OPEN_API_KEY` | 开放接口 ApiKey | 需修改 | | `OPEN_API_SECRET` | 开放接口签名密钥 | 需修改 | | `OPEN_SIGN_SKEW` | 签名时间戳偏差(秒) | `300` | diff --git a/docs/项目分析.md b/docs/项目分析.md index d582777..b999d05 100644 --- a/docs/项目分析.md +++ b/docs/项目分析.md @@ -313,15 +313,40 @@ DB 里的值被静默忽略,且没有任何日志提示。 | `fulfillment.go` 640 行 | `service/fulfillment.go` | 本次未拆;如需可继续按「下单 / 状态机 / 查询 / 超时」拆分 | | `provider_dlc.go` 598 行、`provider_lewan.go` 565 行 | `service/` | 同上,可各拆为「接口适配」与「协议映射」两部分 | -### P2 +### P2 处理结果(2026-10-06 第三轮) + +| 项 | 状态 | 说明 | +|----|------|------| +| SQLite 时代残留 | ✅ 已删除 | `backend/data/affiliate.db`(0 字节)、`app.db`(90 KB)。删除前已确认无代码引用,并保留 `data/uploads/` 下 16 个在用商品图 | +| 告警渠道密钥外泄面 | ✅ 已脱敏 | 响应层只保留密钥末 4 位;webhook 保留域名与路径以便辨认机器人(钉钉/企微在查询串、飞书在路径末段)。脱敏值原样回传时沿用库中真实密钥,填新值则替换。**脱敏刻意放在响应层**:`CheckLowBalanceAndNotify` 复用 `ListAlertChannels` 的结果真正去发消息,在 service 层脱敏会直接打断告警 | +| 三套 HMAC 签名实现重复 | ✅ 已收敛原语 | 新增 `internal/pkg/signutil`,只统一语义完全一致的四类:HMAC-SHA256→hex、body 摘要、时间戳偏差、常数时间比较。**签名串构造刻意不统一**——三者是对外契约且方式不同,合并会直接改变握手结果 | +| 全局改写 `time.Local` | ✅ 已移除 | 见下方说明 | +| 前端公共 chunk 479.7 kB | ⏸ 未处理 | React + AntD 运行时,需评估 antd 按需引入策略,收益与风险需单独评估 | + +**关于时区改动**(这是本轮唯一有行为风险的一项): + +`main.go` 原本用 `time.Local = timeutil.Location()` 隐式改写进程全局状态把时间钉在 +08:00。 + +- 唯一真实依赖是 `rock_snapshot.parseRockTime`:上游返回**不带偏移的北京时间墙面时间**, + 用 `time.Local` 解析等于假设进程时区就是北京时间。实测确认 **pgx 会把 `timestamptz` 解码到 + `time.Local`**,因此该值一旦不是 +08:00,解析结果会整体偏移 8 小时,进而把「本次新绑定」 + 误判为「历史残留」放回库存 → **造成重复发货**。已改为显式 `timeutil.Location()` 并补回归测试。 +- 有一点无法绕开:pgx 的解码行为使接口 JSON 的时间偏移仍由进程时区决定。因此新增 + `timeutil.LocalZoneNotice()`,启动时把不一致显式告警,由运维用 `TZ=Asia/Shanghai` 声明 + (Docker 部署已内置该变量),而不是靠代码偷偷改全局状态。 +- 验证:全量测试在**默认时区 / TZ=UTC / TZ=America/New_York** 三种环境下均通过; + 实测 `TZ=UTC` 启动会打印时区提示,业务时区下静默。 + +> 若要做到「完全不受进程时区影响」的下一个台阶,需要把模型里的 `time.Time` 字段换成 +> 自带 JSON 序列化的自定义时间类型(读写两侧都显式指定时区)。这是一次 API 可见的改动 +> (当前裸字段输出纳秒精度,文档约定的是秒级),建议作为独立迭代并先补接口回归测试。 + +### 仍遗留 | 问题 | 位置 | 说明 | |------|------|------| -| SQLite 时代残留 | `backend/data/affiliate.db`(0 字节)、`app.db`(90 KB) | 已无用途,可删除 | -| 三套 HMAC 签名实现独立 | `middleware/open_auth.go`、`source_open_auth.go`、`pkg/lewan/signer.go` | 重复度高易漂移,可抽出公共签名工具 | -| 告警渠道配置外泄面 | `AlertNotifyChannel.Config` 带 `json:"config"`,内含 webhook token | 对具备 `recharge:manage` 的商户成员可见;建议按需脱敏 | -| 全局改写时区 | `cmd/server/main.go` 中 `time.Local = timeutil.Location()` | 影响进程内所有时间逻辑,建议改用显式 loc | -| 前端仍有较大公共 chunk | `jsx-runtime` 479.7 kB(gzip 157.8 kB) | React + AntD 公共运行时,如需进一步优化可评估 antd 按需引入策略 | +| 前端公共 chunk 479.7 kB(gzip 157.8 kB) | `dist/assets/jsx-runtime-*` | React + AntD 公共运行时;如需进一步压缩需评估 antd 按需引入与手动分包策略 | +| 裸 `time.Time` 字段的 JSON 精度 | 各 model | 输出纳秒精度,而 `docs/API对接关系.md` 约定秒级;契约字段走 `timeutil.FormatAPITime` 是秒级,裸字段未对齐 | ---